Packages etc.
library(dplyr)
library(tidyverse)
library(edgeR)
library(markerGeneProfile)
library(matrixStats)
library(cowplot)
library(broom)
library(knitr)
library(ggpubr)
library(ggrepel)
library(patchwork)
library(ggsignif)
library(modelr)
library(ggbeeswarm)
library(lemon)
theme_set(theme_classic2())
#Colour palette
cbPalette = c("#56B4E9", "#009E73","#E69F00", "#0072B2", "#D55E00", "#CC79A7","#000000","#F0E442")
Loading Data
#Cell type proportions for each data set merged with metadata - ready for modelling
mgp_estimations = read_csv( "/external/rprshnas01/kcni/dkiss/cell_prop_psychiatry/data/psychencode_mgp_estimations.csv")
acc_estimations = read_csv("/external/rprshnas01/kcni/dkiss/cell_prop_psychiatry/data/cmc_acc_mgp_estimations.csv") %>% filter(primaryDiagnosis %in% c("control", "Schizophrenia"))
#Factorize newStudy for aesthetic purposes (this orderl ooks good on graphs)
mgp_estimations = mgp_estimations %>% filter(ageDeath >= 15)
mgp_estimations$newStudy = mgp_estimations$newStudy %>% factor(levels = c("Pitt", "GVEX", "NIMH_HBCC", "LIBD_szControl", "MSSM", "Penn"))
mgp_estimations_long = mgp_estimations %>% pivot_longer(cols = Astrocyte:VLMC, names_to = 'cell_type', values_to = 'rel_prop') %>%
mutate(cell_class = case_when(
cell_type %in% c( "PVALB", "SST", "VIP", "LAMP5", "PAX6") ~ "Inhibitory",
cell_type %in% c("IT", "L4.IT", "L5.ET", "L5.6.IT.Car3", "L5.6.NP", "L6.CT", "L6b") ~ "Excitatory",
TRUE ~ "Non-Neuronal"
))
mgp_estimations_long$cell_type = mgp_estimations_long$cell_type %>% factor(levels = c("PVALB", "SST", "VIP", "LAMP5", "PAX6", "IT", "L4.IT", "L5.ET", "L5.6.IT.Car3", "L5.6.NP", "L6.CT", "L6b", "Astrocyte", "Endothelial", "Microglia", "Oligodendrocyte", "OPC", "Pericyte", "VLMC"))
mgp_estimations_long$cell_class = mgp_estimations_long$cell_class %>% factor(levels = c("Inhibitory", "Excitatory", "Non-Neuronal"))
acc_estimations_long = acc_estimations %>% pivot_longer(cols = Astrocyte:VLMC, names_to = 'cell_type', values_to = 'rel_prop') %>%
mutate(cell_class = case_when(
cell_type %in% c("PVALB", "SST", "VIP", "LAMP5", "PAX6") ~ "Inhibitory",
cell_type %in% c("IT", "L4.IT", "L5.ET", "L5.6.IT.Car3", "L5.6.NP", "L6.CT", "L6b") ~ "Excitatory",
TRUE ~ "Non-Neuronal"
))
acc_estimations_long$cell_type = acc_estimations_long$cell_type %>% factor(levels = c("PVALB", "SST", "VIP", "LAMP5", "PAX6", "IT", "L4.IT", "L5.ET", "L5.6.IT.Car3", "L5.6.NP", "L6.CT", "L6b", "Astrocyte", "Endothelial", "Microglia", "Oligodendrocyte", "OPC", "Pericyte", "VLMC"))
acc_estimations_long$cell_class = acc_estimations_long$cell_class %>% factor(levels = c("Inhibitory", "Excitatory", "Non-Neuronal"))
Load snRNAseq proportions
psychencode_snCTPs = read.csv('/external/rprshnas01/netdata_kcni/stlab/Xiaolin/cell_deconv_data/PsychEncode_label_transferred_snCTP.csv')
snCTP_metadata = read.csv("~/cell_prop_psychiatry/data/psychencode_label_transferred_pseudobulk_metadata.csv")[,-1] %>%
group_by(ID) %>%
mutate(total_cells = sum(num_cells)) %>%
ungroup()
#Add log10 number of cells variable
snCTP_metadata$log10_num_cells = log10(snCTP_metadata$num_cells)
#Add age group variable
snCTP_metadata = snCTP_metadata %>%
mutate(age_group = case_when(
Age < 70 ~ "Under_70", Age >= 70 ~ "Over_70"))
snCTP_metadata$age_group = snCTP_metadata$age_group %>% factor(levels = c("Under_70", "Over_70"))
psychencode_snCTPs = left_join(snCTP_metadata, psychencode_snCTPs)
ling_snCTPs = read.csv("~/cell_prop_psychiatry/data/ling_snCTP_estimations")
Cohort demographic summary
summary_df = mgp_estimations %>%
mutate(individualIDSource = factor(newStudy, levels = c("Pitt", "GVEX", "NIMH_HBCC", "LIBD_szControl", "MSSM", "Penn"))) %>%
group_by(newStudy) %>%
summarise(
CohortName = first(newStudy),
PercentMale = mean(reportedGender == "male", na.rm = TRUE) * 100,
AvgAge = mean(ageDeath, na.rm = TRUE),
AgeSD = sd(ageDeath, na.rm = TRUE),
NumCases = sum(primaryDiagnosis == "Schizophrenia", na.rm = TRUE),
NumControls = sum(primaryDiagnosis == "control", na.rm = TRUE)
)
Residualize MGPs based on linear model
mgp_estimations_long = mgp_estimations_long %>%
group_by(cell_type) %>%
mutate(resid = resid(lm(scale(rel_prop) ~ scale(RIN) + scale(ageDeath) + scale(PMI) +
reportedGender))) %>%
ungroup()
Model rCTPs based on diagnosis and covariates seperately in each cohort
combined_lms = mgp_estimations_long %>%
group_by(newStudy, cell_type) %>%
do(tidy(lm(scale(rel_prop) ~ scale(RIN) + scale(ageDeath) + scale(PMI) +
reportedGender + primaryDiagnosis, data = .))) %>%
ungroup() %>%
mutate(padj = p.adjust(p.value, method = 'fdr'),
term = recode(term,
`(Intercept)` = "Intercept",
`reportedGendermale` = "gender:Male",
`primaryDiagnosisSchizophrenia` = "SCZ",
`scale(ageDeath)` = "Age",
`scale(PMI)` = "PMI",
`scale(RIN)` = "RIN")) %>%
merge(unique(mgp_estimations_long[c('dataset', 'newStudy')]), by.x = "newStudy") %>%
merge(unique(mgp_estimations_long[c('cell_class', 'cell_type')]), by.x = "cell_type") %>%
mutate(newStudy = factor(newStudy, levels = c("Pitt", "GVEX", "NIMH_HBCC", "LIBD_szControl", "MSSM", "Penn")))
Interaction Models: As outlined above, there appears to be an interaction between age x diagnosis with respect to SST cell proportion. In other words, SST cell proportion is generally lower as ageDeath increases, but declines faster with age in controls. To analyze this more clearly, we create new models (interaction_lms) to visualize this effect. These models include new_study as a covariate, and age x diagnosis as an interactor.
interaction_lms = mgp_estimations_long %>%
group_by(newStudy, cell_type) %>%
do(tidy(lm(scale(rel_prop) ~ scale(RIN) + scale(ageDeath) + scale(PMI) +
reportedGender + primaryDiagnosis + scale(ageDeath) * primaryDiagnosis, data = .))) %>%
ungroup() %>%
mutate(padj = p.adjust(`p.value`, method = 'fdr'),
term = recode(term,
`(Intercept)` = "Intercept",
`reportedGendermale` = "gender:Male",
`primaryDiagnosisSchizophrenia` = "SCZ",
`scale(ageDeath):primaryDiagnosisSchizophrenia` = "Age x Diagnosis",
`scale(ageDeath)` = "Age",
`scale(PMI)` = "PMI",
`scale(RIN)` = "RIN")) %>%
merge(unique(mgp_estimations_long[c('dataset', 'newStudy')]), by.x = "newStudy") %>%
merge(unique(mgp_estimations_long[c('cell_class', 'cell_type')]), by.x = "cell_type") %>%
mutate(newStudy = factor(newStudy, levels = c("Pitt", "GVEX", "NIMH_HBCC", "LIBD_szControl", "MSSM", "Penn")))
Interaction mega-analysis w/ all data
mega_lms = mgp_estimations_long %>%
group_by(cell_type) %>%
do(tidy(lm(scale(rel_prop) ~ scale(RIN) + scale(ageDeath) + scale(PMI) +
reportedGender + primaryDiagnosis + newStudy + scale(ageDeath)*primaryDiagnosis, data = .))) %>%
ungroup() %>%
mutate(padj = p.adjust(`p.value`, method = 'fdr'),
term = recode(term,
`(Intercept)` = "Intercept",
`reportedGendermale` = "gender:Male",
`primaryDiagnosisSchizophrenia` = "SCZ",
`scale(ageDeath)` = "Age",
`scale(ageDeath):primaryDiagnosisSchizophrenia` = "Age x Diagnosis",
`scale(PMI)` = "PMI",
`newStudyNIMH_HBCC` = "NIMH_HBCC",
`newStudyLIBD_szControl` = "LIBD_szControl",
`newStudyPitt` = "Pitt",
`newStudyMSSM` = "MSSM",
`newStudyPenn` = "Penn",
`scale(RIN)` = "RIN")) %>%
merge(unique(mgp_estimations_long[c('cell_class', 'cell_type')]), by.x = "cell_type")
Functions to generate age range bins
# Create function to map age to age range
age_to_range = function(age) {
if(age >= 15 & age < 30) {
return("15-29")
} else {
lower_bound = floor((age - 30) / 10) * 10 + 30
upper_bound = lower_bound + 9
return(paste0(lower_bound, "-", upper_bound))
}
}
# Create function to bin ages to >=70 and <70
classify_ages = function(ages) {
classifications = ifelse(ages <= 70, "Under 70", "Over 70")
return(classifications)
}
Create age groups from age 20-90 in increments of 10 years. Plot meganalysis beta coeffs against age group.
mgp_estimations_long$age_range = sapply(mgp_estimations_long$ageDeath, age_to_range)
mega_lms_by_age = mgp_estimations_long %>%
group_by(age_range, cell_type) %>%
do(tidy(lm(scale(rel_prop) ~ scale(RIN) + scale(PMI) +
reportedGender + primaryDiagnosis + newStudy, data = .))) %>%
ungroup() %>%
mutate(padj = p.adjust(`p.value`, method = 'fdr'),
term = recode(term,
`(Intercept)` = "Intercept",
`reportedGendermale` = "gender:Male",
`primaryDiagnosisSchizophrenia` = "SCZ",
`scale(ageDeath)` = "Age",
`scale(PMI)` = "PMI",
`newStudyNIMH_HBCC` = "NIMH_HBCC",
`newStudyPitt` = "Pitt",
`newStudyMSSM` = "MSSM",
`newStudyPenn` = "Penn",
`scale(RIN)` = "RIN")) %>%
merge(unique(mgp_estimations_long[c('cell_class', 'cell_type')]), by.x = "cell_type")
Bulk RNAseq Meganalysis based on age +/- 70
mgp_estimations_long$age_class <- sapply(mgp_estimations_long$ageDeath, classify_ages) %>% factor(levels = c("Under 70", "Over 70"))
mega_lms_by_age_class = mgp_estimations_long %>%
group_by(age_class, cell_type) %>%
do(tidy(lm(scale(rel_prop) ~ scale(RIN) + scale(PMI) +
reportedGender + newStudy + primaryDiagnosis, data = .))) %>%
ungroup() %>%
mutate(padj = p.adjust(`p.value`, method = 'fdr'),
term = recode(term,
`(Intercept)` = "Intercept",
`reportedGendermale` = "gender:Male",
`primaryDiagnosisSchizophrenia` = "SCZ",
`scale(ageDeath)` = "Age",
`scale(PMI)` = "PMI",
`newStudyNIMH_HBCC` = "NIMH_HBCC",
`newStudyPitt` = "Pitt",
`newStudyMSSM` = "MSSM",
`newStudyPenn` = "Penn",
`scale(RIN)` = "RIN")) %>%
merge(unique(mgp_estimations_long[c('cell_class', 'cell_type')]), by.x = "cell_type")
#same but stratify by cohort
lms_by_age_class = mgp_estimations_long %>%
group_by(age_class, cell_type, newStudy) %>%
do(tidy(lm(scale(rel_prop) ~ scale(RIN) + scale(PMI) +
reportedGender + primaryDiagnosis, data = .))) %>%
ungroup() %>%
mutate(padj = p.adjust(`p.value`, method = 'fdr'),
term = recode(term,
`(Intercept)` = "Intercept",
`reportedGendermale` = "gender:Male",
`primaryDiagnosisSchizophrenia` = "SCZ",
`scale(ageDeath)` = "Age",
`scale(PMI)` = "PMI",
`newStudyNIMH_HBCC` = "NIMH_HBCC",
`newStudyPitt` = "Pitt",
`newStudyMSSM` = "MSSM",
`newStudyPenn` = "Penn",
`scale(RIN)` = "RIN")) %>%
merge(unique(mgp_estimations_long[c('cell_class', 'cell_type')]), by.x = "cell_type")
Single Cell Meganalysis with interaction model
snCTPs_long = psychencode_snCTPs %>%
pivot_longer(cols = Astrocyte:VIP, names_to = 'cell_type', values_to = 'rel_prop',) %>%
mutate(cell_class = case_when(
cell_type %in% c("LAMP5", "PAX6", "PVALB", "SST", "VIP") ~ "Inhibitory",
cell_type %in% c("IT", "L4.IT", "L5.ET", "L5.6.IT.Car3", "L5.6.NP", "L6.CT", "L6b") ~ "Excitatory",
TRUE ~ "Non-Neuronal"
)) %>%
distinct(cell_type, unique_donor_ID, .keep_all = T)
sn_mega_lms = snCTPs_long %>% filter(total_cells >= 500) %>%
group_by(cell_type) %>%
do(tidy(lm(scale(rel_prop) ~ scale(PMI) + Gender + Institution + Phenotype + scale(Age) * Phenotype, data = .))) %>%
ungroup() %>%
mutate(padj = p.adjust(`p.value`, method = 'fdr')) %>%
# add cell class labels
mutate(cell_class = case_when(
cell_type %in% c("LAMP5", "PAX6", "PVALB", "SST", "VIP") ~ "Inhibitory",
cell_type %in% c("IT", "L4.IT", "L5.ET", "L5.6.IT.Car3", "L5.6.NP", "L6.CT", "L6b") ~ "Excitatory",
TRUE ~ "Non-Neuronal"
)) %>%
mutate(term = recode(term,
`(Intercept)` = "Intercept",
`reportedGendermale` = "gender:Male",
`PhenotypeSZ` = "SCZ",
`primaryDiagnosisBipolar Disorder` = "BP",
`DxMDD` = "MDD",
`scale(Age)` = "Age",
`scale(PMI)` = "PMI",
`newStudyNIMH_HBCC` = "NIMH_HBCC",
`newStudyPitt` = "Pitt",
`newStudyMSSM` = "MSSM",
`newStudyPenn` = "Penn",
`scale(RIN)` = "RIN",
`PhenotypeSZ:scale(Age)` = "Interaction"
))
Single Cell Meganalysis based on age +/- 70
sn_mega_lms_by_age = snCTPs_long %>% filter(total_cells >= 500) %>%
group_by(cell_type, age_group) %>%
do(tidy(lm(scale(rel_prop) ~ scale(PMI) + Gender + Institution + Phenotype, data = .))) %>%
ungroup() %>%
mutate(padj = p.adjust(`p.value`, method = 'fdr')) %>%
mutate(term = recode(term,
`(Intercept)` = "Intercept",
`reportedGendermale` = "gender:Male",
`PhenotypeSZ` = "SCZ",
`primaryDiagnosisBipolar Disorder` = "BP",
`DxMDD` = "MDD",
`scale(ageDeath)` = "Age",
`scale(PMI)` = "PMI",
`newStudyNIMH_HBCC` = "NIMH_HBCC",
`newStudyPitt` = "Pitt",
`newStudyMSSM` = "MSSM",
`newStudyPenn` = "Penn",
`scale(RIN)` = "RIN"))
#again but stratified by cohort
sn_lms_by_age = snCTPs_long %>% filter(total_cells >= 500) %>%
group_by(cell_type, age_group, Institution) %>%
do(tidy(lm(scale(rel_prop) ~ scale(PMI) + Gender + Phenotype, data = .))) %>%
ungroup() %>%
mutate(padj = p.adjust(`p.value`, method = 'fdr')) %>%
mutate(term = recode(term,
`(Intercept)` = "Intercept",
`reportedGendermale` = "gender:Male",
`PhenotypeSZ` = "SCZ",
`primaryDiagnosisBipolar Disorder` = "BP",
`DxMDD` = "MDD",
`scale(ageDeath)` = "Age",
`scale(PMI)` = "PMI",
`newStudyNIMH_HBCC` = "NIMH_HBCC",
`newStudyPitt` = "Pitt",
`newStudyMSSM` = "MSSM",
`newStudyPenn` = "Penn",
`scale(RIN)` = "RIN"))
————////FIGURES////————
Figure 1: Illustrating the association between cell prop and diagnosis and cohort specific differences
significant_combined = subset(combined_lms, padj < 0.1 & cell_type %in% c("SST", "PVALB", "VIP") & term == "SCZ")
significant_combined$asterisks <- ifelse(significant_combined$padj <= 0.01, "***",
ifelse(significant_combined$padj <= 0.05, "**", "*"))
significant_combined$vjust <- ifelse(significant_combined$estimate >= 0, -3, 7)
figure_1b = combined_lms %>%
filter(term == 'SCZ', cell_type %in% c("SST", "PVALB", "VIP")) %>%
mutate(cell_type = fct_reorder(cell_type, estimate)) %>%
ggplot(aes(x = newStudy, y = estimate, fill = newStudy)) +
geom_hline(yintercept = 0) +
geom_bar(stat = "identity", position = "dodge") +
geom_errorbar(aes(ymin = estimate - std.error, ymax = estimate + std.error)) +
ylab('Beta Coefficient') +
xlab('') +
scale_fill_manual(values = cbPalette) +
geom_text(data = significant_combined,
aes(newStudy, estimate, label = asterisks),
vjust = significant_combined$vjust) +
theme(
axis.text.x = element_blank(),
axis.text.y = element_text(size = 15),
axis.title.x = element_text(vjust = 6, size = 15),
axis.title.y = element_text(size = 16),
strip.text.x = element_text(size = 14),
axis.line.x = element_blank(),
axis.ticks.x = element_blank()) +
facet_grid(~cell_type, drop = T, scale = "free_x", space = "free") +
guides(fill = guide_none()) +
scale_y_continuous(limits = c(-0.9, 0.5))
# Figure 1c: Residualized MGPs vs. Diagnosis
figure_1c <- mgp_estimations_long %>%
filter(cell_type %in% c("SST", "PVALB", "VIP")) %>%
ggplot(aes(x = primaryDiagnosis, y = resid)) +
geom_boxplot(outlier.shape = NA, aes(fill = newStudy, alpha = 0.5), show.legend = F, notch = T) +
geom_beeswarm(size = 1, alpha = 0.3, aes(colour = newStudy), show.legend = F) +
ylab('Residualized CTPs') +
xlab('Diagnosis') +
scale_x_discrete(labels = c("C", "SCZ")) +
scale_colour_manual(values = cbPalette) +
scale_fill_manual(values = cbPalette) +
theme(
axis.text.x = element_text(size = 15),
axis.text.y = element_text(size = 15),
axis.title.x = element_text(vjust = -2, size = 15),
axis.title.y = element_text(size = 15),
strip.text.x = element_text(size = 14),
strip.text.y = element_text(size = 14)) +
guides(fill = FALSE) +
facet_grid(rows = vars(cell_type), cols = vars(newStudy), drop = T, scale = "free", switch = "y", space = "free") +
geom_signif(comparisons = list(c("control", "Schizophrenia")), map_signif_level = TRUE, , test = wilcox.test) +
scale_y_continuous(limits = c(-3.5, 3.5))
Warning: The `<scale>` argument of `guides()` cannot be `FALSE`. Use "none" instead as of ggplot2 3.3.4.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
#Figure 1d - Hisotgram of cohort ages
figure_1d = mgp_estimations %>%
ggplot(aes(x = ageDeath)) +
geom_histogram(aes(fill = newStudy)) +
facet_wrap(~ newStudy, nrow = 6, scales = "free_y") +
labs(x = "ageDeath", y = "Frequency") +
scale_fill_manual(values = c("#56B4E9", "#009E73","#E69F00", "#0072B2", "#D55E00", "#CC79A7")) +
scale_y_continuous(n.breaks = 4) +
guides(fill = guide_none()) +
theme(panel.grid = element_blank(),
strip.background = element_blank(),
strip.text = element_text(size = rel(0.9), face = "plain"),
axis.text.x = element_text(size = 13,),
axis.text.y = element_text(size = 13),
axis.title.x = element_text( size = 15),
axis.title.y = element_text(size = 16),
strip.text.x = element_text(size = 14))
# Combine Figure 1
figure_1_top = (figure_1b | figure_1d) + plot_layout(widths = c(4,2))
figure_1_bottom = figure_1c
figure_1 = figure_1_top / figure_1_bottom + plot_annotation(tag_levels = 'A') + plot_layout(heights = c(1, 1)) & theme(plot.tag = element_text(size = 25))
figure_1
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning: Removed 11 rows containing non-finite outside the scale range (`stat_boxplot()`).
Warning: Removed 11 rows containing non-finite outside the scale range (`stat_signif()`).
Warning: Removed 11 rows containing missing values or values outside the scale range (`geom_point()`).

#Save to plots folder
output_filename <- "~/cell_prop_psychiatry/plots/figure_1.jpg"
# Use ggsave to save the plot as a JPG image
ggsave(output_filename, figure_1, width = 11, height = 13, dpi = 500)
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.
Warning: Removed 11 rows containing non-finite outside the scale range (`stat_boxplot()`).
Warning: Removed 11 rows containing non-finite outside the scale range (`stat_signif()`).
Warning: Removed 11 rows containing missing values or values outside the scale range (`geom_point()`).
Figure 2: Introducing and presenting an interaction between age and diagnosis w/rt cell proportion.
# Plot interaction beta values from meganalysis
significant_mega = subset(mega_lms, padj <= 0.1 & term == "Age x Diagnosis" & cell_type %in% c("Astrocyte", "IT", "L5.6.NP", "LAMP5", "Microglia", "Oligodendrocyte", "OPC", "Pericyte", "PVALB", "SST", "VIP" ))
significant_mega$asterisks = ifelse(significant_mega$padj <= 0.01, "***",
ifelse(significant_mega$padj <= 0.05, "**", "*"))
significant_mega$vjust = ifelse(significant_mega$estimate >= 0, -3, 3)
# Figure 2a: Results from interaction meganalysis stratified by cell class - plot interaction beta coeff
mega_lms$cell_type = mega_lms$cell_type %>% factor(levels = c("PVALB", "SST", "VIP", "LAMP5", "PAX6", "IT", "L4.IT", "L5.ET", "L5.6.IT.Car3", "L5.6.NP", "L6.CT", "L6b", "Astrocyte", "Endothelial", "Microglia", "Oligodendrocyte", "OPC", "Pericyte", "VLMC"))
figure_2a = mega_lms %>% filter(cell_type %in% c("Astrocyte", "IT", "L5.6.NP", "LAMP5", "Microglia", "Oligodendrocyte", "OPC","Pericyte", "PVALB", "SST", "VIP" )) %>%
filter(term %in% 'Age x Diagnosis') %>%
ggplot(aes(x = cell_type, y = estimate)) +
geom_hline(yintercept = 0) +
geom_bar(stat = "identity", position = "dodge") +
facet_grid(~cell_class, drop = T, scale = "free_x", space = "free") +
geom_errorbar(aes(ymin = estimate - std.error, ymax = estimate + std.error)) +
ylab('Interaction Coefficient') +
xlab('Cell Type') +
geom_text(data = significant_mega,
aes(cell_type, estimate, label = asterisks),
vjust = significant_mega$vjust) +
theme(
axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1, size = 12),
axis.text.y = element_text(size = 15),
axis.title.x = element_text(vjust = 8, size = 15),
axis.title.y = element_text(size = 15),
strip.text.x = element_text(size = 14)) +
guides(fill = FALSE) +
scale_y_continuous(limits = c(-0.15, 0.32))
# Figure 2b: Plot beta values from meganalysis in 10-year age bins for SST, PVALB, VIP
significant_mega_age = subset(mega_lms_by_age, padj <= 0.1 & term == "SCZ" & cell_type %in% c("SST", "PVALB", "VIP"))
significant_mega_age$asterisks = ifelse(significant_mega_age$padj <= 0.01, "***",
ifelse(significant_mega_age$padj <= 0.05, "**", "*"))
significant_mega_age$vjust = ifelse(significant_mega_age$estimate >= 0, -2, 3)
figure_2b = mega_lms_by_age %>%
filter(term %in% 'SCZ' & cell_type %in% c("SST", "PVALB", "VIP")) %>%
mutate(cell_type = fct_reorder(cell_type, estimate)) %>%
ggplot(aes(x = age_range, y = estimate)) +
geom_hline(yintercept = 0) +
geom_bar(stat = "identity", position = "dodge") +
geom_errorbar(aes(ymin = estimate - std.error, ymax = estimate + std.error)) +
ylab('Beta Coefficient') +
xlab('\n \n Age Range') +
geom_text(data = significant_mega_age,
aes(age_range, estimate, label = asterisks),
vjust = significant_mega_age$vjust) +
theme(
axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1, size = 13),
axis.text.y = element_text(size = 15),
axis.title.x = element_text(vjust = 8, size = 15),
axis.title.y = element_text(size = 15),
strip.text.x = element_text(size = 14)) +
facet_wrap(~cell_type, drop = T, scale = "free_x") +
guides(fill = FALSE) +
scale_y_continuous(limits = c(-1.0, 1.4))
# Figure 2c: Visualizing interaction between age and diagnosis by plotting rel_prop vs. age in each diagnosis
figure_2c = mgp_estimations_long %>% filter(ageDeath < 90) %>%
filter(cell_type %in% c("SST", "PVALB", "VIP")) %>%
ggplot(aes(x = ageDeath, y = rel_prop, color = primaryDiagnosis)) +
geom_point(alpha = 0.5, size = 0.5) +
geom_smooth(se = F, method = 'lm', fullrange = T) +
ylab('Relative Cell Prop.') +
xlab('Age') +
theme(
axis.text.x = element_text(size = 15),
axis.text.y = element_text(size = 15),
axis.title.x = element_text(vjust = -2, size = 15),
axis.title.y = element_text(size = 15),
legend.position = c(0.9, 0.9),
legend.title = element_blank(),
legend.text = element_text(size = 12),
strip.text.x = element_text(size = 14)) +
facet_grid(~cell_type, drop = T, scale = "free_x", space = "free") +
guides(fill = guide_legend(nrow = 1)) +
scale_y_continuous(limits = c(-3, 3)) +
scale_color_manual(values = c("dodgerblue2", "firebrick2"), labels=c('CON', 'SCZ'))
Warning: A numeric `legend.position` argument in `theme()` was deprecated in ggplot2 3.5.0.
ℹ Please use the `legend.position.inside` argument of `theme()` instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
#Figure 2d: Mega-analysis results when binned as +/- 70 years
significant_mega_age_class = subset(mega_lms_by_age_class, padj <= 0.1 & term == "SCZ" & cell_type %in% c("SST", "PVALB", "VIP"))
significant_mega_age_class$asterisks = ifelse(significant_mega_age_class$padj <= 0.01, "***", ifelse(significant_mega_age_class$padj <= 0.05, "**", "*"))
significant_mega_age_class$vjust = ifelse(significant_mega_age_class$estimate >= 0, -3, 3)
figure_2d = mega_lms_by_age_class %>%
filter(term %in% 'SCZ' & cell_type %in% c("SST", "PVALB", "VIP")) %>%
mutate(cell_type = fct_reorder(cell_type, estimate)) %>%
ggplot(aes(x = age_class, y = estimate, fill = age_class)) +
geom_hline(yintercept = 0) +
geom_bar(stat = "identity", position = "dodge") +
geom_errorbar(aes(ymin = estimate - std.error, ymax = estimate + std.error)) +
ylab('Beta Coefficient') +
xlab('\n \n Age Range') +
scale_fill_manual(values = c("mediumpurple3", "olivedrab3"), labels=c('Above 70', 'Below 70')) +
geom_text(data = significant_mega_age_class,
aes(age_class, estimate, label = asterisks),
vjust = significant_mega_age_class$vjust) +
theme(
axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1, size = 13),
axis.text.y = element_text(size = 15),
axis.title.x = element_text(vjust = 8, size = 15),
axis.title.y = element_text(size = 15),
strip.text.x = element_text(size = 14)) +
facet_wrap(~cell_type, drop = T, scale = "free_x") +
guides(fill = FALSE) +
scale_y_continuous(limits = c(-0.55, 0.55))
figure_2_middle = (figure_2b +figure_2d) + plot_layout(widths = c(2,1))
figure_2 = figure_2c / figure_2_middle / figure_2a
figure_2 = figure_2 + plot_annotation(tag_levels = 'A') + plot_layout(heights = c(1, 1, 1)) & theme(plot.tag = element_text(size = 25))
figure_2
`geom_smooth()` using formula = 'y ~ x'
Warning: Removed 53 rows containing non-finite outside the scale range (`stat_smooth()`).
Warning: Removed 53 rows containing missing values or values outside the scale range (`geom_point()`).

#Save to plots folder
output_filename <- "~/cell_prop_psychiatry/plots/figure_2.jpg"
# Use ggsave to save the plot as a JPG image
ggsave(output_filename, figure_2, width = 13, height = 15, dpi = 500)
`geom_smooth()` using formula = 'y ~ x'
Warning: Removed 53 rows containing non-finite outside the scale range (`stat_smooth()`).
Warning: Removed 53 rows containing missing values or values outside the scale range (`geom_point()`).
Figure 3: Validating trends and interactions in ssRNAseq
# Figure 3a: snRNAseq cell proportions binned by age
significant_sn_mega_by_age <- subset(sn_mega_lms_by_age, padj <= 0.1 & term == "SCZ" & cell_type %in% c("SST", "PVALB", "VIP"))
significant_sn_mega_by_age$asterisks <- ifelse(significant_sn_mega_by_age$padj <= 0.01, "***",
ifelse(significant_sn_mega_by_age$padj <= 0.05, "**", "*"))
significant_sn_mega_by_age$vjust <- ifelse(significant_sn_mega_by_age$estimate >= 0, -5, 5)
figure_3a <- sn_mega_lms_by_age %>%
filter(term %in% 'SCZ' & cell_type %in% c("SST", "PVALB", "VIP")) %>%
mutate(cell_type = fct_reorder(cell_type, estimate)) %>%
ggplot(aes(x = age_group, y = estimate, fill = age_group)) +
geom_hline(yintercept = 0) +
geom_bar(stat = "identity", position = "dodge") +
geom_errorbar(aes(ymin = estimate - std.error, ymax = estimate + std.error)) +
ylab('Beta Coefficient') +
xlab('Age Range (Above/Below 70)') +
scale_fill_manual(values = c("mediumpurple3", "olivedrab3"), labels = c('Above 70', 'Below 70')) +
geom_text(data = significant_sn_mega_by_age,
aes(age_group, estimate, label = asterisks),
vjust = significant_sn_mega_by_age$vjust) +
theme(
axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1, size = 13),
axis.text.y = element_text(size = 15),
axis.title.x = element_text(vjust = 1, size = 15),
axis.title.y = element_text(size = 15),
strip.text.x = element_text(size = 14),
legend.position = "none") +
scale_y_continuous(limits = c(-1, 1)) +
facet_wrap(~cell_type, drop = TRUE, scale = "free")
# Figure 3b: Relative cell proportions from snCTPs
figure_3b <- snCTPs_long %>% filter(total_cells >= 500) %>%
filter(Phenotype %in% c('SZ', "CON"), cell_type %in% c("SST", "PVALB", "VIP")) %>%
ggplot(aes(x = Age, y = rel_prop, color = Phenotype)) +
geom_point(alpha = 0.8, size = 1.3) +
geom_smooth(se = F, method = 'lm') +
scale_colour_hue(labels = c("Control", "SCZ")) +
ylab('Relative Cell Prop.') +
xlab('Age') +
theme(
axis.text.x = element_text(size = 13),
axis.text.y = element_text(size = 13),
axis.title.x = element_text(, size = 16),
axis.title.y = element_text(size = 16),
legend.position = c(0.94, 0.94),
legend.title = element_blank(),
legend.text = element_text(size = 12),
strip.text.x = element_text(size = 15),
strip.text.y = element_text(size = 15)
) +
facet_rep_grid(rows = vars(Institution),
cols = vars(cell_type),
drop = T,
scale = "free",
switch = "y",
space = "free_x",
repeat.tick.labels = T
) +
guides(fill = guide_legend(nrow = 1)) +
scale_color_manual(values = c("dodgerblue2", "firebrick2"), labels=c('CON', 'SCZ'))
Scale for colour is already present.
Adding another scale for colour, which will replace the existing scale.
# Figure 3c: Interaction terms for snRNAseq results
significant_sn_mega <- subset(sn_mega_lms, padj <= 0.1 & term == "Interaction" & cell_type %in% c("Astrocyte", "IT", "L5.6.NP", "LAMP5", "Microglia", "Oligodendrocyte", "OPC","Pericyte", "PVALB", "SST", "VIP" ))
significant_sn_mega$asterisks <- ifelse(significant_sn_mega$padj <= 0.01, "***",
ifelse(significant_sn_mega$padj <= 0.05, "**", "*"))
significant_sn_mega$vjust <- ifelse(significant_sn_mega$estimate >= 0, -7, 7)
sn_mega_lms$cell_type = sn_mega_lms$cell_type %>% factor(levels = c("PVALB", "SST", "VIP", "LAMP5", "PAX6", "IT", "L4.IT", "L5.ET", "L5.6.IT.Car3", "L5.6.NP", "L6.CT", "L6b", "Astrocyte", "Endothelial", "Microglia", "Oligodendrocyte", "OPC", "Pericyte", "VLMC"))
figure_3c = sn_mega_lms %>% filter(cell_type %in% c("Astrocyte", "IT", "L5.6.NP", "LAMP5", "Microglia", "Pericyte", "PVALB", "SST", "VIP" )) %>%
filter(term %in% 'Interaction') %>%
ggplot(aes(x = cell_type, y = estimate)) +
geom_hline(yintercept = 0) +
geom_bar(stat = "identity", position = "dodge") +
geom_errorbar(aes(ymin = estimate - std.error, ymax = estimate + std.error)) +
facet_grid(~cell_class, drop = T, scale = "free_x", space = "free") +
ylab('Interaction Coefficient') +
xlab('Cell Type') +
geom_text(data = significant_sn_mega,
aes(cell_type, estimate, label = asterisks),
vjust = significant_sn_mega$vjust) +
theme(
axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1, size = 12),
axis.text.y = element_text(size = 15),
axis.title.x = element_text(vjust = -2, size = 15),
axis.title.y = element_text(size = 15),
strip.text.x = element_text(size = 15)) +
guides(fill = FALSE) +
scale_y_continuous(limits = c(-0.45, 0.65))
# Figure 3: Combined figure
figure_3_top = figure_3b
figure_3_bottom = figure_3c + figure_3a + plot_layout(widths = c(2,1))
figure_3 <- figure_3_top/figure_3_bottom
figure_3 = figure_3 + plot_annotation(tag_levels = 'A') + plot_layout(heights = c(2,1)) & theme(plot.tag = element_text(size = 25))
figure_3
`geom_smooth()` using formula = 'y ~ x'

#Save to plots folder
output_filename <- "~/cell_prop_psychiatry/plots/figure_3.jpg"
# Use ggsave to save the plot as a JPG image
ggsave(output_filename, figure_3, width = 13, height = 16, dpi = 500)
`geom_smooth()` using formula = 'y ~ x'
Figure 5: AgeOnset vs. MGP
#Duration of Illness
mgp_estimations_long$durationIllness <- mgp_estimations_long$ageDeath - mgp_estimations_long$ageOnset
CELL_TYPES = c("SST", "PVALB", "VIP")
DATASETS = c("GVEX", "LIBD_szControl")
ageOnset_residuals_data = matrix(ncol = (ncol(mgp_estimations_long)+1), nrow = 0) %>% data.frame()
colnames(ageOnset_residuals_data) = c(colnames(mgp_estimations_long), "resid")
for(DATASET in DATASETS) {
for(CELL_TYPE in CELL_TYPES) {
#Create res_model without ageOnset variable
res_model_data = mgp_estimations_long %>% filter(primaryDiagnosis %in% c('Schizophrenia'), cell_type == CELL_TYPE , dataset == DATASET, is.na(ageOnset) == F)
#Model without ageOnset
res_model = lm(scale(rel_prop) ~ scale(ageDeath) + scale(RIN) + scale(PMI) + reportedGender, data = res_model_data)
#Calculate residuals and add column to res_model_data
res_model_data = res_model_data %>% add_residuals(var = "resid", model = res_model)
ageOnset_residuals_data = ageOnset_residuals_data %>% rbind(res_model_data)
}
}
figure_5 <- ageOnset_residuals_data %>%
ggplot(aes(x = ageOnset, y = resid, color = dataset)) +
geom_point(alpha = 0.5, size = 0.8) +
geom_smooth(se = FALSE, method = 'lm', fullrange = TRUE) +
ylab('rCTP residuals (AU)') +
xlab('Age of SCZ onset') +
theme(
axis.text.x = element_text(size = 16),
axis.text.y = element_text(size = 16),
axis.title.x = element_text(vjust = -2, size = 16, margin = margin(t = 8)),
axis.title.y = element_text(size = 16),
strip.text = element_text(size = 16),
legend.text = element_text(size = 16),
plot.margin = margin(10, 10, 20, 10)) +
facet_grid(cols = vars(cell_type), rows = vars(dataset)) +
scale_y_continuous(limits = c(-1.5, 1.5)) +
scale_color_manual(values = c("dodgerblue2", "firebrick2")) +
guides(color = guide_legend(title = NULL)) +
stat_cor(aes(label = paste(..r.label.., ..p.label.., sep = "~")), color = "black", geom = "label")
figure_5
Warning: The dot-dot notation (`..r.label..`) was deprecated in ggplot2 3.4.0.
ℹ Please use `after_stat(r.label)` instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
`geom_smooth()` using formula = 'y ~ x'
Warning: Removed 34 rows containing non-finite outside the scale range (`stat_smooth()`).
Warning: Removed 34 rows containing non-finite outside the scale range (`stat_cor()`).
Warning: Removed 34 rows containing missing values or values outside the scale range (`geom_point()`).

#Save to plots folder
output_filename <- "~/cell_prop_psychiatry/plots/figure_5.jpg"
# Use ggsave to save the plot as a JPG image
ggsave(output_filename, figure_5, width = 9, height = 7, dpi = 500)
`geom_smooth()` using formula = 'y ~ x'
Warning: Removed 34 rows containing non-finite outside the scale range (`stat_smooth()`).
Warning: Removed 34 rows containing non-finite outside the scale range (`stat_cor()`).
Warning: Removed 34 rows containing missing values or values outside the scale range (`geom_point()`).
LS0tCnRpdGxlOiAicHN5Y2hlbmNvZGVfZmlndXJlc19jbGVhbmVkIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpQYWNrYWdlcyBldGMuCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShlZGdlUikKbGlicmFyeShtYXJrZXJHZW5lUHJvZmlsZSkgCmxpYnJhcnkobWF0cml4U3RhdHMpCmxpYnJhcnkoY293cGxvdCkgCmxpYnJhcnkoYnJvb20pCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkoZ2dwdWJyKQpsaWJyYXJ5KGdncmVwZWwpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KGdnc2lnbmlmKQpsaWJyYXJ5KG1vZGVscikKbGlicmFyeShnZ2JlZXN3YXJtKQpsaWJyYXJ5KGxlbW9uKQp0aGVtZV9zZXQodGhlbWVfY2xhc3NpYzIoKSkKI0NvbG91ciBwYWxldHRlCmNiUGFsZXR0ZSA9IGMoIiM1NkI0RTkiLCAiIzAwOUU3MyIsIiNFNjlGMDAiLCAiIzAwNzJCMiIsICIjRDU1RTAwIiwgIiNDQzc5QTciLCIjMDAwMDAwIiwiI0YwRTQ0MiIpCmBgYAoKTG9hZGluZyBEYXRhCmBgYHtyfQojQ2VsbCB0eXBlIHByb3BvcnRpb25zIGZvciBlYWNoIGRhdGEgc2V0IG1lcmdlZCB3aXRoIG1ldGFkYXRhIC0gcmVhZHkgZm9yIG1vZGVsbGluZyAKbWdwX2VzdGltYXRpb25zID0gcmVhZF9jc3YoICIvZXh0ZXJuYWwvcnByc2huYXMwMS9rY25pL2RraXNzL2NlbGxfcHJvcF9wc3ljaGlhdHJ5L2RhdGEvcHN5Y2hlbmNvZGVfbWdwX2VzdGltYXRpb25zLmNzdiIpIAphY2NfZXN0aW1hdGlvbnMgPSByZWFkX2NzdigiL2V4dGVybmFsL3JwcnNobmFzMDEva2NuaS9ka2lzcy9jZWxsX3Byb3BfcHN5Y2hpYXRyeS9kYXRhL2NtY19hY2NfbWdwX2VzdGltYXRpb25zLmNzdiIpICU+JSBmaWx0ZXIocHJpbWFyeURpYWdub3NpcyAlaW4lIGMoImNvbnRyb2wiLCAiU2NoaXpvcGhyZW5pYSIpKQojRmFjdG9yaXplIG5ld1N0dWR5IGZvciBhZXN0aGV0aWMgcHVycG9zZXMgKHRoaXMgb3JkZXJsIG9va3MgZ29vZCBvbiBncmFwaHMpCm1ncF9lc3RpbWF0aW9ucyA9IG1ncF9lc3RpbWF0aW9ucyAlPiUgZmlsdGVyKGFnZURlYXRoID49IDE1KQptZ3BfZXN0aW1hdGlvbnMkbmV3U3R1ZHkgPSBtZ3BfZXN0aW1hdGlvbnMkbmV3U3R1ZHkgJT4lIGZhY3RvcihsZXZlbHMgPSBjKCJQaXR0IiwgIkdWRVgiLCAiTklNSF9IQkNDIiwgIkxJQkRfc3pDb250cm9sIiwgIk1TU00iLCAiUGVubiIpKQptZ3BfZXN0aW1hdGlvbnNfbG9uZyA9IG1ncF9lc3RpbWF0aW9ucyAlPiUgcGl2b3RfbG9uZ2VyKGNvbHMgPSBBc3Ryb2N5dGU6VkxNQywgbmFtZXNfdG8gPSAnY2VsbF90eXBlJywgdmFsdWVzX3RvID0gJ3JlbF9wcm9wJykgJT4lCiAgbXV0YXRlKGNlbGxfY2xhc3MgPSBjYXNlX3doZW4oCiAgICBjZWxsX3R5cGUgJWluJSBjKCAiUFZBTEIiLCAiU1NUIiwgIlZJUCIsICJMQU1QNSIsICJQQVg2IikgfiAiSW5oaWJpdG9yeSIsCiAgICBjZWxsX3R5cGUgJWluJSBjKCJJVCIsICJMNC5JVCIsICJMNS5FVCIsICJMNS42LklULkNhcjMiLCAiTDUuNi5OUCIsICJMNi5DVCIsICJMNmIiKSB+ICJFeGNpdGF0b3J5IiwKICAgIFRSVUUgfiAiTm9uLU5ldXJvbmFsIgogICkpCm1ncF9lc3RpbWF0aW9uc19sb25nJGNlbGxfdHlwZSA9IG1ncF9lc3RpbWF0aW9uc19sb25nJGNlbGxfdHlwZSAlPiUgZmFjdG9yKGxldmVscyA9IGMoIlBWQUxCIiwgIlNTVCIsICJWSVAiLCAiTEFNUDUiLCAiUEFYNiIsICJJVCIsICJMNC5JVCIsICJMNS5FVCIsICJMNS42LklULkNhcjMiLCAiTDUuNi5OUCIsICJMNi5DVCIsICJMNmIiLCAiQXN0cm9jeXRlIiwgIkVuZG90aGVsaWFsIiwgIk1pY3JvZ2xpYSIsICJPbGlnb2RlbmRyb2N5dGUiLCAiT1BDIiwgIlBlcmljeXRlIiwgIlZMTUMiKSkKbWdwX2VzdGltYXRpb25zX2xvbmckY2VsbF9jbGFzcyA9IG1ncF9lc3RpbWF0aW9uc19sb25nJGNlbGxfY2xhc3MgJT4lIGZhY3RvcihsZXZlbHMgPSBjKCJJbmhpYml0b3J5IiwgIkV4Y2l0YXRvcnkiLCAiTm9uLU5ldXJvbmFsIikpCgphY2NfZXN0aW1hdGlvbnNfbG9uZyA9IGFjY19lc3RpbWF0aW9ucyAlPiUgcGl2b3RfbG9uZ2VyKGNvbHMgPSBBc3Ryb2N5dGU6VkxNQywgbmFtZXNfdG8gPSAnY2VsbF90eXBlJywgdmFsdWVzX3RvID0gJ3JlbF9wcm9wJykgJT4lCiAgbXV0YXRlKGNlbGxfY2xhc3MgPSBjYXNlX3doZW4oCiAgICBjZWxsX3R5cGUgJWluJSBjKCJQVkFMQiIsICJTU1QiLCAiVklQIiwgIkxBTVA1IiwgIlBBWDYiKSB+ICJJbmhpYml0b3J5IiwKICAgIGNlbGxfdHlwZSAlaW4lIGMoIklUIiwgIkw0LklUIiwgIkw1LkVUIiwgIkw1LjYuSVQuQ2FyMyIsICJMNS42Lk5QIiwgIkw2LkNUIiwgIkw2YiIpIH4gIkV4Y2l0YXRvcnkiLAogICAgVFJVRSB+ICJOb24tTmV1cm9uYWwiCiAgKSkKYWNjX2VzdGltYXRpb25zX2xvbmckY2VsbF90eXBlID0gYWNjX2VzdGltYXRpb25zX2xvbmckY2VsbF90eXBlICU+JSBmYWN0b3IobGV2ZWxzID0gYygiUFZBTEIiLCAiU1NUIiwgIlZJUCIsICJMQU1QNSIsICJQQVg2IiwgIklUIiwgIkw0LklUIiwgIkw1LkVUIiwgIkw1LjYuSVQuQ2FyMyIsICJMNS42Lk5QIiwgIkw2LkNUIiwgIkw2YiIsICJBc3Ryb2N5dGUiLCAiRW5kb3RoZWxpYWwiLCAiTWljcm9nbGlhIiwgIk9saWdvZGVuZHJvY3l0ZSIsICJPUEMiLCAiUGVyaWN5dGUiLCAiVkxNQyIpKQphY2NfZXN0aW1hdGlvbnNfbG9uZyRjZWxsX2NsYXNzID0gYWNjX2VzdGltYXRpb25zX2xvbmckY2VsbF9jbGFzcyAlPiUgZmFjdG9yKGxldmVscyA9IGMoIkluaGliaXRvcnkiLCAiRXhjaXRhdG9yeSIsICJOb24tTmV1cm9uYWwiKSkKYGBgCgpMb2FkIHNuUk5Bc2VxIHByb3BvcnRpb25zCmBgYHtyfQpwc3ljaGVuY29kZV9zbkNUUHMgPSByZWFkLmNzdignL2V4dGVybmFsL3JwcnNobmFzMDEvbmV0ZGF0YV9rY25pL3N0bGFiL1hpYW9saW4vY2VsbF9kZWNvbnZfZGF0YS9Qc3ljaEVuY29kZV9sYWJlbF90cmFuc2ZlcnJlZF9zbkNUUC5jc3YnKQpzbkNUUF9tZXRhZGF0YSA9IHJlYWQuY3N2KCJ+L2NlbGxfcHJvcF9wc3ljaGlhdHJ5L2RhdGEvcHN5Y2hlbmNvZGVfbGFiZWxfdHJhbnNmZXJyZWRfcHNldWRvYnVsa19tZXRhZGF0YS5jc3YiKVssLTFdICU+JQogIGdyb3VwX2J5KElEKSAlPiUKICBtdXRhdGUodG90YWxfY2VsbHMgPSBzdW0obnVtX2NlbGxzKSkgJT4lCiAgdW5ncm91cCgpIAojQWRkIGxvZzEwIG51bWJlciBvZiBjZWxscyB2YXJpYWJsZQpzbkNUUF9tZXRhZGF0YSRsb2cxMF9udW1fY2VsbHMgPSBsb2cxMChzbkNUUF9tZXRhZGF0YSRudW1fY2VsbHMpCiNBZGQgYWdlIGdyb3VwIHZhcmlhYmxlCnNuQ1RQX21ldGFkYXRhID0gIHNuQ1RQX21ldGFkYXRhICU+JQogIG11dGF0ZShhZ2VfZ3JvdXAgPSBjYXNlX3doZW4oCiAgICBBZ2UgPCA3MCB+ICJVbmRlcl83MCIsIEFnZSA+PSA3MCB+ICJPdmVyXzcwIikpIApzbkNUUF9tZXRhZGF0YSRhZ2VfZ3JvdXAgPSBzbkNUUF9tZXRhZGF0YSRhZ2VfZ3JvdXAgJT4lIGZhY3RvcihsZXZlbHMgPSBjKCJVbmRlcl83MCIsICJPdmVyXzcwIikpCnBzeWNoZW5jb2RlX3NuQ1RQcyA9IGxlZnRfam9pbihzbkNUUF9tZXRhZGF0YSwgcHN5Y2hlbmNvZGVfc25DVFBzKSAKYGBgCgpgYGB7cn0KbGluZ19zbkNUUHMgPSByZWFkLmNzdigifi9jZWxsX3Byb3BfcHN5Y2hpYXRyeS9kYXRhL2xpbmdfc25DVFBfZXN0aW1hdGlvbnMiKQpgYGAKCkNvaG9ydCBkZW1vZ3JhcGhpYyBzdW1tYXJ5CmBgYHtyfQpzdW1tYXJ5X2RmID0gbWdwX2VzdGltYXRpb25zICU+JQogIG11dGF0ZShpbmRpdmlkdWFsSURTb3VyY2UgPSBmYWN0b3IobmV3U3R1ZHksIGxldmVscyA9IGMoIlBpdHQiLCAiR1ZFWCIsICJOSU1IX0hCQ0MiLCAiTElCRF9zekNvbnRyb2wiLCAiTVNTTSIsICJQZW5uIikpKSAlPiUKICAgIGdyb3VwX2J5KG5ld1N0dWR5KSAlPiUKICAgIHN1bW1hcmlzZSgKICAgICAgICBDb2hvcnROYW1lID0gZmlyc3QobmV3U3R1ZHkpLAogICAgICAgIFBlcmNlbnRNYWxlID0gbWVhbihyZXBvcnRlZEdlbmRlciA9PSAibWFsZSIsIG5hLnJtID0gVFJVRSkgKiAxMDAsCiAgICAgICAgQXZnQWdlID0gbWVhbihhZ2VEZWF0aCwgbmEucm0gPSBUUlVFKSwKICAgICAgICBBZ2VTRCA9IHNkKGFnZURlYXRoLCBuYS5ybSA9IFRSVUUpLAogICAgICAgIE51bUNhc2VzID0gc3VtKHByaW1hcnlEaWFnbm9zaXMgPT0gIlNjaGl6b3BocmVuaWEiLCBuYS5ybSA9IFRSVUUpLAogICAgICAgIE51bUNvbnRyb2xzID0gc3VtKHByaW1hcnlEaWFnbm9zaXMgPT0gImNvbnRyb2wiLCBuYS5ybSA9IFRSVUUpCiAgICApCmBgYAoKUmVzaWR1YWxpemUgTUdQcyBiYXNlZCBvbiBsaW5lYXIgbW9kZWwKYGBge3J9Cm1ncF9lc3RpbWF0aW9uc19sb25nID0gbWdwX2VzdGltYXRpb25zX2xvbmcgJT4lCiAgZ3JvdXBfYnkoY2VsbF90eXBlKSAlPiUKICBtdXRhdGUocmVzaWQgPSByZXNpZChsbShzY2FsZShyZWxfcHJvcCkgfiBzY2FsZShSSU4pICsgc2NhbGUoYWdlRGVhdGgpICsgc2NhbGUoUE1JKSArIAogICAgICAgICAgICAgICAgcmVwb3J0ZWRHZW5kZXIpKSkgJT4lCiAgdW5ncm91cCgpCmBgYAoKCk1vZGVsIHJDVFBzIGJhc2VkIG9uIGRpYWdub3NpcyBhbmQgY292YXJpYXRlcyBzZXBlcmF0ZWx5IGluIGVhY2ggY29ob3J0CmBgYHtyfQpjb21iaW5lZF9sbXMgPSBtZ3BfZXN0aW1hdGlvbnNfbG9uZyAlPiUKICBncm91cF9ieShuZXdTdHVkeSwgY2VsbF90eXBlKSAlPiUKICBkbyh0aWR5KGxtKHNjYWxlKHJlbF9wcm9wKSB+IHNjYWxlKFJJTikgKyBzY2FsZShhZ2VEZWF0aCkgKyBzY2FsZShQTUkpICsKICAgICAgICAgICAgICAgcmVwb3J0ZWRHZW5kZXIgKyBwcmltYXJ5RGlhZ25vc2lzLCBkYXRhID0gLikpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKHBhZGogPSBwLmFkanVzdChwLnZhbHVlLCBtZXRob2QgPSAnZmRyJyksCiAgICAgICAgIHRlcm0gPSByZWNvZGUodGVybSwKICAgICAgICAgICAgICAgICAgICAgICBgKEludGVyY2VwdClgID0gIkludGVyY2VwdCIsCiAgICAgICAgICAgICAgICAgICAgICAgYHJlcG9ydGVkR2VuZGVybWFsZWAgPSAiZ2VuZGVyOk1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgIGBwcmltYXJ5RGlhZ25vc2lzU2NoaXpvcGhyZW5pYWAgPSAiU0NaIiwKICAgICAgICAgICAgICAgICAgICAgICBgc2NhbGUoYWdlRGVhdGgpYCA9ICJBZ2UiLAogICAgICAgICAgICAgICAgICAgICAgIGBzY2FsZShQTUkpYCA9ICJQTUkiLAogICAgICAgICAgICAgICAgICAgICAgIGBzY2FsZShSSU4pYCA9ICJSSU4iKSkgJT4lCiAgbWVyZ2UodW5pcXVlKG1ncF9lc3RpbWF0aW9uc19sb25nW2MoJ2RhdGFzZXQnLCAnbmV3U3R1ZHknKV0pLCBieS54ID0gIm5ld1N0dWR5IikgJT4lCiAgbWVyZ2UodW5pcXVlKG1ncF9lc3RpbWF0aW9uc19sb25nW2MoJ2NlbGxfY2xhc3MnLCAnY2VsbF90eXBlJyldKSwgYnkueCA9ICJjZWxsX3R5cGUiKSAlPiUKICBtdXRhdGUobmV3U3R1ZHkgPSBmYWN0b3IobmV3U3R1ZHksIGxldmVscyA9IGMoIlBpdHQiLCAiR1ZFWCIsICJOSU1IX0hCQ0MiLCAiTElCRF9zekNvbnRyb2wiLCAiTVNTTSIsICJQZW5uIikpKQpgYGAKCkludGVyYWN0aW9uIE1vZGVsczogQXMgb3V0bGluZWQgYWJvdmUsIHRoZXJlIGFwcGVhcnMgdG8gYmUgYW4gaW50ZXJhY3Rpb24gYmV0d2VlbiBhZ2UgeCBkaWFnbm9zaXMgd2l0aCByZXNwZWN0IHRvIFNTVCBjZWxsIHByb3BvcnRpb24uIEluIG90aGVyIHdvcmRzLCBTU1QgY2VsbCBwcm9wb3J0aW9uIGlzIGdlbmVyYWxseSBsb3dlciBhcyBhZ2VEZWF0aCBpbmNyZWFzZXMsIGJ1dCBkZWNsaW5lcyBmYXN0ZXIgd2l0aCBhZ2UgaW4gY29udHJvbHMuIFRvIGFuYWx5emUgdGhpcyBtb3JlIGNsZWFybHksIHdlIGNyZWF0ZSBuZXcgbW9kZWxzIChpbnRlcmFjdGlvbl9sbXMpIHRvIHZpc3VhbGl6ZSB0aGlzIGVmZmVjdC4gVGhlc2UgbW9kZWxzIGluY2x1ZGUgbmV3X3N0dWR5IGFzIGEgY292YXJpYXRlLCBhbmQgYWdlIHggZGlhZ25vc2lzIGFzIGFuIGludGVyYWN0b3IuCmBgYHtyfQppbnRlcmFjdGlvbl9sbXMgPSBtZ3BfZXN0aW1hdGlvbnNfbG9uZyAlPiUKICBncm91cF9ieShuZXdTdHVkeSwgY2VsbF90eXBlKSAlPiUKICBkbyh0aWR5KGxtKHNjYWxlKHJlbF9wcm9wKSB+IHNjYWxlKFJJTikgKyBzY2FsZShhZ2VEZWF0aCkgKyBzY2FsZShQTUkpICsKICAgICAgICAgICAgICAgcmVwb3J0ZWRHZW5kZXIgKyBwcmltYXJ5RGlhZ25vc2lzICsgc2NhbGUoYWdlRGVhdGgpICogcHJpbWFyeURpYWdub3NpcywgZGF0YSA9IC4pKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZShwYWRqID0gcC5hZGp1c3QoYHAudmFsdWVgLCBtZXRob2QgPSAnZmRyJyksCiAgICAgICAgIHRlcm0gPSByZWNvZGUodGVybSwKICAgICAgICAgICAgICAgICAgICAgICBgKEludGVyY2VwdClgID0gIkludGVyY2VwdCIsCiAgICAgICAgICAgICAgICAgICAgICAgYHJlcG9ydGVkR2VuZGVybWFsZWAgPSAiZ2VuZGVyOk1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgIGBwcmltYXJ5RGlhZ25vc2lzU2NoaXpvcGhyZW5pYWAgPSAiU0NaIiwKICAgICAgICAgICAgICAgICAgICAgICBgc2NhbGUoYWdlRGVhdGgpOnByaW1hcnlEaWFnbm9zaXNTY2hpem9waHJlbmlhYCA9ICJBZ2UgeCBEaWFnbm9zaXMiLAogICAgICAgICAgICAgICAgICAgICAgIGBzY2FsZShhZ2VEZWF0aClgID0gIkFnZSIsCiAgICAgICAgICAgICAgICAgICAgICAgYHNjYWxlKFBNSSlgID0gIlBNSSIsCiAgICAgICAgICAgICAgICAgICAgICAgYHNjYWxlKFJJTilgID0gIlJJTiIpKSAlPiUKICBtZXJnZSh1bmlxdWUobWdwX2VzdGltYXRpb25zX2xvbmdbYygnZGF0YXNldCcsICduZXdTdHVkeScpXSksIGJ5LnggPSAibmV3U3R1ZHkiKSAlPiUKICBtZXJnZSh1bmlxdWUobWdwX2VzdGltYXRpb25zX2xvbmdbYygnY2VsbF9jbGFzcycsICdjZWxsX3R5cGUnKV0pLCBieS54ID0gImNlbGxfdHlwZSIpICU+JQogIG11dGF0ZShuZXdTdHVkeSA9IGZhY3RvcihuZXdTdHVkeSwgbGV2ZWxzID0gYygiUGl0dCIsICJHVkVYIiwgIk5JTUhfSEJDQyIsICJMSUJEX3N6Q29udHJvbCIsICJNU1NNIiwgIlBlbm4iKSkpCgpgYGAKCkludGVyYWN0aW9uIG1lZ2EtYW5hbHlzaXMgdy8gYWxsIGRhdGEgCmBgYHtyfQptZWdhX2xtcyA9IG1ncF9lc3RpbWF0aW9uc19sb25nICU+JSAKICBncm91cF9ieShjZWxsX3R5cGUpICU+JQogIGRvKHRpZHkobG0oc2NhbGUocmVsX3Byb3ApIH4gc2NhbGUoUklOKSArIHNjYWxlKGFnZURlYXRoKSArIHNjYWxlKFBNSSkgKwogICAgICAgICAgICAgcmVwb3J0ZWRHZW5kZXIgKyBwcmltYXJ5RGlhZ25vc2lzICsgbmV3U3R1ZHkgKyBzY2FsZShhZ2VEZWF0aCkqcHJpbWFyeURpYWdub3NpcywgZGF0YSA9IC4pKSkgJT4lCiAgdW5ncm91cCgpICU+JSAKICBtdXRhdGUocGFkaiA9IHAuYWRqdXN0KGBwLnZhbHVlYCwgbWV0aG9kID0gJ2ZkcicpLAogICAgICAgICB0ZXJtID0gcmVjb2RlKHRlcm0sCiAgICAgICAgICAgICAgICAgICAgICAgYChJbnRlcmNlcHQpYCA9ICJJbnRlcmNlcHQiLAogICAgICAgICAgICAgICAgICAgICAgIGByZXBvcnRlZEdlbmRlcm1hbGVgID0gImdlbmRlcjpNYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICBgcHJpbWFyeURpYWdub3Npc1NjaGl6b3BocmVuaWFgID0gIlNDWiIsCgogICAgICAgICAgICAgICAgICAgICAgIGBzY2FsZShhZ2VEZWF0aClgID0gIkFnZSIsCiAgICAgICAgICAgICAgICAgICAgICAgYHNjYWxlKGFnZURlYXRoKTpwcmltYXJ5RGlhZ25vc2lzU2NoaXpvcGhyZW5pYWAgPSAiQWdlIHggRGlhZ25vc2lzIiwKICAgICAgICAgICAgICAgICAgICAgICBgc2NhbGUoUE1JKWAgPSAiUE1JIiwgCiAgICAgICAgICAgICAgICAgICAgICAgYG5ld1N0dWR5TklNSF9IQkNDYCA9ICJOSU1IX0hCQ0MiLAogICAgICAgICAgICAgICAgICAgICAgIGBuZXdTdHVkeUxJQkRfc3pDb250cm9sYCA9ICJMSUJEX3N6Q29udHJvbCIsCiAgICAgICAgICAgICAgICAgICAgICAgYG5ld1N0dWR5UGl0dGAgPSAiUGl0dCIsCiAgICAgICAgICAgICAgICAgICAgICAgYG5ld1N0dWR5TVNTTWAgPSAiTVNTTSIsCiAgICAgICAgICAgICAgICAgICAgICAgYG5ld1N0dWR5UGVubmAgPSAiUGVubiIsCiAgICAgICAgICAgICAgICAgICAgICAgYHNjYWxlKFJJTilgID0gIlJJTiIpKSAlPiUKICBtZXJnZSh1bmlxdWUobWdwX2VzdGltYXRpb25zX2xvbmdbYygnY2VsbF9jbGFzcycsICdjZWxsX3R5cGUnKV0pLCBieS54ID0gImNlbGxfdHlwZSIpIApgYGAKCkZ1bmN0aW9ucyB0byAgZ2VuZXJhdGUgYWdlIHJhbmdlIGJpbnMgCmBgYHtyfQojIENyZWF0ZSBmdW5jdGlvbiB0byBtYXAgYWdlIHRvIGFnZSByYW5nZQphZ2VfdG9fcmFuZ2UgPSBmdW5jdGlvbihhZ2UpIHsKICAgIGlmKGFnZSA+PSAxNSAmIGFnZSA8IDMwKSB7CiAgICByZXR1cm4oIjE1LTI5IikKICB9IGVsc2UgewogICAgbG93ZXJfYm91bmQgPSBmbG9vcigoYWdlIC0gMzApIC8gMTApICogMTAgKyAzMAogICAgdXBwZXJfYm91bmQgPSBsb3dlcl9ib3VuZCArIDkKICAgIHJldHVybihwYXN0ZTAobG93ZXJfYm91bmQsICItIiwgdXBwZXJfYm91bmQpKQogIH0KfQojIENyZWF0ZSBmdW5jdGlvbiB0byBiaW4gYWdlcyB0byA+PTcwIGFuZCA8NzAKY2xhc3NpZnlfYWdlcyA9IGZ1bmN0aW9uKGFnZXMpIHsKICBjbGFzc2lmaWNhdGlvbnMgPSBpZmVsc2UoYWdlcyA8PSA3MCwgIlVuZGVyIDcwIiwgIk92ZXIgNzAiKQogIHJldHVybihjbGFzc2lmaWNhdGlvbnMpCn0KYGBgCgoKQ3JlYXRlIGFnZSBncm91cHMgZnJvbSBhZ2UgMjAtOTAgaW4gaW5jcmVtZW50cyBvZiAxMCB5ZWFycy4gUGxvdCBtZWdhbmFseXNpcyBiZXRhIGNvZWZmcyBhZ2FpbnN0IGFnZSBncm91cC4KYGBge3J9Cm1ncF9lc3RpbWF0aW9uc19sb25nJGFnZV9yYW5nZSA9IHNhcHBseShtZ3BfZXN0aW1hdGlvbnNfbG9uZyRhZ2VEZWF0aCwgYWdlX3RvX3JhbmdlKQoKbWVnYV9sbXNfYnlfYWdlID0gbWdwX2VzdGltYXRpb25zX2xvbmcgJT4lIAogIGdyb3VwX2J5KGFnZV9yYW5nZSwgY2VsbF90eXBlKSAlPiUKICBkbyh0aWR5KGxtKHNjYWxlKHJlbF9wcm9wKSB+IHNjYWxlKFJJTikgKyBzY2FsZShQTUkpICsKICAgICAgICAgICAgIHJlcG9ydGVkR2VuZGVyICsgcHJpbWFyeURpYWdub3NpcyArIG5ld1N0dWR5LCBkYXRhID0gLikpKSAlPiUKICB1bmdyb3VwKCkgJT4lIAogIG11dGF0ZShwYWRqID0gcC5hZGp1c3QoYHAudmFsdWVgLCBtZXRob2QgPSAnZmRyJyksCiAgICAgICAgIHRlcm0gPSByZWNvZGUodGVybSwKICAgICAgICAgICAgICAgICAgICAgICBgKEludGVyY2VwdClgID0gIkludGVyY2VwdCIsCiAgICAgICAgICAgICAgICAgICAgICAgYHJlcG9ydGVkR2VuZGVybWFsZWAgPSAiZ2VuZGVyOk1hbGUiLAogICAgICAgICAgICAgICAgICAgICAgIGBwcmltYXJ5RGlhZ25vc2lzU2NoaXpvcGhyZW5pYWAgPSAiU0NaIiwKICAgICAgICAgICAgICAgICAgICAgICBgc2NhbGUoYWdlRGVhdGgpYCA9ICJBZ2UiLAogICAgICAgICAgICAgICAgICAgICAgIGBzY2FsZShQTUkpYCA9ICJQTUkiLCAKICAgICAgICAgICAgICAgICAgICAgICBgbmV3U3R1ZHlOSU1IX0hCQ0NgID0gIk5JTUhfSEJDQyIsCiAgICAgICAgICAgICAgICAgICAgICAgYG5ld1N0dWR5UGl0dGAgPSAiUGl0dCIsCiAgICAgICAgICAgICAgICAgICAgICAgYG5ld1N0dWR5TVNTTWAgPSAiTVNTTSIsCiAgICAgICAgICAgICAgICAgICAgICAgYG5ld1N0dWR5UGVubmAgPSAiUGVubiIsCiAgICAgICAgICAgICAgICAgICAgICAgYHNjYWxlKFJJTilgID0gIlJJTiIpKSAlPiUKICBtZXJnZSh1bmlxdWUobWdwX2VzdGltYXRpb25zX2xvbmdbYygnY2VsbF9jbGFzcycsICdjZWxsX3R5cGUnKV0pLCBieS54ID0gImNlbGxfdHlwZSIpIApgYGAKCkJ1bGsgUk5Bc2VxIE1lZ2FuYWx5c2lzIGJhc2VkIG9uIGFnZSArLy0gNzAKYGBge3J9Cm1ncF9lc3RpbWF0aW9uc19sb25nJGFnZV9jbGFzcyA8LSBzYXBwbHkobWdwX2VzdGltYXRpb25zX2xvbmckYWdlRGVhdGgsIGNsYXNzaWZ5X2FnZXMpICU+JSBmYWN0b3IobGV2ZWxzID0gYygiVW5kZXIgNzAiLCAiT3ZlciA3MCIpKQptZWdhX2xtc19ieV9hZ2VfY2xhc3MgPSBtZ3BfZXN0aW1hdGlvbnNfbG9uZyAlPiUgCiAgZ3JvdXBfYnkoYWdlX2NsYXNzLCBjZWxsX3R5cGUpICU+JQogIGRvKHRpZHkobG0oc2NhbGUocmVsX3Byb3ApIH4gc2NhbGUoUklOKSArIHNjYWxlKFBNSSkgKwogICAgICAgICAgICAgcmVwb3J0ZWRHZW5kZXIgKyBuZXdTdHVkeSArIHByaW1hcnlEaWFnbm9zaXMsIGRhdGEgPSAuKSkpICU+JQogIHVuZ3JvdXAoKSAlPiUgCiAgbXV0YXRlKHBhZGogPSBwLmFkanVzdChgcC52YWx1ZWAsIG1ldGhvZCA9ICdmZHInKSwKICAgICAgICAgdGVybSA9IHJlY29kZSh0ZXJtLAogICAgICAgICAgICAgICAgICAgICAgIGAoSW50ZXJjZXB0KWAgPSAiSW50ZXJjZXB0IiwKICAgICAgICAgICAgICAgICAgICAgICBgcmVwb3J0ZWRHZW5kZXJtYWxlYCA9ICJnZW5kZXI6TWFsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgYHByaW1hcnlEaWFnbm9zaXNTY2hpem9waHJlbmlhYCA9ICJTQ1oiLAogICAgICAgICAgICAgICAgICAgICAgIGBzY2FsZShhZ2VEZWF0aClgID0gIkFnZSIsCiAgICAgICAgICAgICAgICAgICAgICAgYHNjYWxlKFBNSSlgID0gIlBNSSIsIAogICAgICAgICAgICAgICAgICAgICAgIGBuZXdTdHVkeU5JTUhfSEJDQ2AgPSAiTklNSF9IQkNDIiwKICAgICAgICAgICAgICAgICAgICAgICBgbmV3U3R1ZHlQaXR0YCA9ICJQaXR0IiwKICAgICAgICAgICAgICAgICAgICAgICBgbmV3U3R1ZHlNU1NNYCA9ICJNU1NNIiwKICAgICAgICAgICAgICAgICAgICAgICBgbmV3U3R1ZHlQZW5uYCA9ICJQZW5uIiwKICAgICAgICAgICAgICAgICAgICAgICBgc2NhbGUoUklOKWAgPSAiUklOIikpICU+JQogIG1lcmdlKHVuaXF1ZShtZ3BfZXN0aW1hdGlvbnNfbG9uZ1tjKCdjZWxsX2NsYXNzJywgJ2NlbGxfdHlwZScpXSksIGJ5LnggPSAiY2VsbF90eXBlIikgCgojc2FtZSBidXQgc3RyYXRpZnkgYnkgY29ob3J0Cmxtc19ieV9hZ2VfY2xhc3MgPSBtZ3BfZXN0aW1hdGlvbnNfbG9uZyAlPiUgCiAgZ3JvdXBfYnkoYWdlX2NsYXNzLCBjZWxsX3R5cGUsIG5ld1N0dWR5KSAlPiUKICBkbyh0aWR5KGxtKHNjYWxlKHJlbF9wcm9wKSB+IHNjYWxlKFJJTikgKyBzY2FsZShQTUkpICsKICAgICAgICAgICAgIHJlcG9ydGVkR2VuZGVyICsgcHJpbWFyeURpYWdub3NpcywgZGF0YSA9IC4pKSkgJT4lCiAgdW5ncm91cCgpICU+JSAKICBtdXRhdGUocGFkaiA9IHAuYWRqdXN0KGBwLnZhbHVlYCwgbWV0aG9kID0gJ2ZkcicpLAogICAgICAgICB0ZXJtID0gcmVjb2RlKHRlcm0sCiAgICAgICAgICAgICAgICAgICAgICAgYChJbnRlcmNlcHQpYCA9ICJJbnRlcmNlcHQiLAogICAgICAgICAgICAgICAgICAgICAgIGByZXBvcnRlZEdlbmRlcm1hbGVgID0gImdlbmRlcjpNYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICBgcHJpbWFyeURpYWdub3Npc1NjaGl6b3BocmVuaWFgID0gIlNDWiIsCiAgICAgICAgICAgICAgICAgICAgICAgYHNjYWxlKGFnZURlYXRoKWAgPSAiQWdlIiwKICAgICAgICAgICAgICAgICAgICAgICBgc2NhbGUoUE1JKWAgPSAiUE1JIiwgCiAgICAgICAgICAgICAgICAgICAgICAgYG5ld1N0dWR5TklNSF9IQkNDYCA9ICJOSU1IX0hCQ0MiLAogICAgICAgICAgICAgICAgICAgICAgIGBuZXdTdHVkeVBpdHRgID0gIlBpdHQiLAogICAgICAgICAgICAgICAgICAgICAgIGBuZXdTdHVkeU1TU01gID0gIk1TU00iLAogICAgICAgICAgICAgICAgICAgICAgIGBuZXdTdHVkeVBlbm5gID0gIlBlbm4iLAogICAgICAgICAgICAgICAgICAgICAgIGBzY2FsZShSSU4pYCA9ICJSSU4iKSkgJT4lCiAgbWVyZ2UodW5pcXVlKG1ncF9lc3RpbWF0aW9uc19sb25nW2MoJ2NlbGxfY2xhc3MnLCAnY2VsbF90eXBlJyldKSwgYnkueCA9ICJjZWxsX3R5cGUiKSAKCgpgYGAKClNpbmdsZSBDZWxsIE1lZ2FuYWx5c2lzIHdpdGggaW50ZXJhY3Rpb24gbW9kZWwKYGBge3J9CnNuQ1RQc19sb25nID0gcHN5Y2hlbmNvZGVfc25DVFBzICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gQXN0cm9jeXRlOlZJUCwgbmFtZXNfdG8gPSAnY2VsbF90eXBlJywgdmFsdWVzX3RvID0gJ3JlbF9wcm9wJywpICAlPiUKICBtdXRhdGUoY2VsbF9jbGFzcyA9IGNhc2Vfd2hlbigKICAgIGNlbGxfdHlwZSAlaW4lIGMoIkxBTVA1IiwgIlBBWDYiLCAiUFZBTEIiLCAiU1NUIiwgIlZJUCIpIH4gIkluaGliaXRvcnkiLAogICAgY2VsbF90eXBlICVpbiUgYygiSVQiLCAiTDQuSVQiLCAiTDUuRVQiLCAiTDUuNi5JVC5DYXIzIiwgIkw1LjYuTlAiLCAiTDYuQ1QiLCAiTDZiIikgfiAiRXhjaXRhdG9yeSIsCiAgICBUUlVFIH4gIk5vbi1OZXVyb25hbCIKICApKSAlPiUgIAogIGRpc3RpbmN0KGNlbGxfdHlwZSwgdW5pcXVlX2Rvbm9yX0lELCAua2VlcF9hbGwgPSBUKQoKCnNuX21lZ2FfbG1zID0gc25DVFBzX2xvbmcgJT4lIGZpbHRlcih0b3RhbF9jZWxscyA+PSA1MDApICU+JQogIGdyb3VwX2J5KGNlbGxfdHlwZSkgJT4lCiAgZG8odGlkeShsbShzY2FsZShyZWxfcHJvcCkgfiBzY2FsZShQTUkpICsgR2VuZGVyICsgSW5zdGl0dXRpb24gKyBQaGVub3R5cGUgKyBzY2FsZShBZ2UpICogUGhlbm90eXBlLCBkYXRhID0gLikpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKHBhZGogPSBwLmFkanVzdChgcC52YWx1ZWAsIG1ldGhvZCA9ICdmZHInKSkgJT4lCiAgIyBhZGQgY2VsbCBjbGFzcyBsYWJlbHMKICBtdXRhdGUoY2VsbF9jbGFzcyA9IGNhc2Vfd2hlbigKICAgIGNlbGxfdHlwZSAlaW4lIGMoIkxBTVA1IiwgIlBBWDYiLCAiUFZBTEIiLCAiU1NUIiwgIlZJUCIpIH4gIkluaGliaXRvcnkiLAogICAgY2VsbF90eXBlICVpbiUgYygiSVQiLCAiTDQuSVQiLCAiTDUuRVQiLCAiTDUuNi5JVC5DYXIzIiwgIkw1LjYuTlAiLCAiTDYuQ1QiLCAiTDZiIikgfiAiRXhjaXRhdG9yeSIsCiAgICBUUlVFIH4gIk5vbi1OZXVyb25hbCIKICApKSAlPiUKICBtdXRhdGUodGVybSA9IHJlY29kZSh0ZXJtLAogICAgICAgICAgICAgICAgICAgICAgIGAoSW50ZXJjZXB0KWAgPSAiSW50ZXJjZXB0IiwKICAgICAgICAgICAgICAgICAgICAgICBgcmVwb3J0ZWRHZW5kZXJtYWxlYCA9ICJnZW5kZXI6TWFsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgYFBoZW5vdHlwZVNaYCA9ICJTQ1oiLAogICAgICAgICAgICAgICAgICAgICAgIGBwcmltYXJ5RGlhZ25vc2lzQmlwb2xhciBEaXNvcmRlcmAgPSAiQlAiLAogICAgICAgICAgICAgICAgICAgICAgIGBEeE1ERGAgPSAiTUREIiwKICAgICAgICAgICAgICAgICAgICAgICBgc2NhbGUoQWdlKWAgPSAiQWdlIiwKICAgICAgICAgICAgICAgICAgICAgICBgc2NhbGUoUE1JKWAgPSAiUE1JIiwgCiAgICAgICAgICAgICAgICAgICAgICAgYG5ld1N0dWR5TklNSF9IQkNDYCA9ICJOSU1IX0hCQ0MiLAogICAgICAgICAgICAgICAgICAgICAgIGBuZXdTdHVkeVBpdHRgID0gIlBpdHQiLAogICAgICAgICAgICAgICAgICAgICAgIGBuZXdTdHVkeU1TU01gID0gIk1TU00iLAogICAgICAgICAgICAgICAgICAgICAgIGBuZXdTdHVkeVBlbm5gID0gIlBlbm4iLAogICAgICAgICAgICAgICAgICAgICAgIGBzY2FsZShSSU4pYCA9ICJSSU4iLAogICAgICAgICAgICAgICAgICAgICAgIGBQaGVub3R5cGVTWjpzY2FsZShBZ2UpYCA9ICJJbnRlcmFjdGlvbiIKKSkKCmBgYAoKU2luZ2xlIENlbGwgTWVnYW5hbHlzaXMgYmFzZWQgb24gYWdlICsvLSA3MApgYGB7cn0Kc25fbWVnYV9sbXNfYnlfYWdlID0gc25DVFBzX2xvbmcgJT4lIGZpbHRlcih0b3RhbF9jZWxscyA+PSA1MDApICU+JQogIGdyb3VwX2J5KGNlbGxfdHlwZSwgYWdlX2dyb3VwKSAlPiUKICBkbyh0aWR5KGxtKHNjYWxlKHJlbF9wcm9wKSB+IHNjYWxlKFBNSSkgKyBHZW5kZXIgKyBJbnN0aXR1dGlvbiArIFBoZW5vdHlwZSwgZGF0YSA9IC4pKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIG11dGF0ZShwYWRqID0gcC5hZGp1c3QoYHAudmFsdWVgLCBtZXRob2QgPSAnZmRyJykpICU+JQogIG11dGF0ZSh0ZXJtID0gcmVjb2RlKHRlcm0sCiAgICAgICAgICAgICAgICAgICAgICAgYChJbnRlcmNlcHQpYCA9ICJJbnRlcmNlcHQiLAogICAgICAgICAgICAgICAgICAgICAgIGByZXBvcnRlZEdlbmRlcm1hbGVgID0gImdlbmRlcjpNYWxlIiwKICAgICAgICAgICAgICAgICAgICAgICBgUGhlbm90eXBlU1pgID0gIlNDWiIsCiAgICAgICAgICAgICAgICAgICAgICAgYHByaW1hcnlEaWFnbm9zaXNCaXBvbGFyIERpc29yZGVyYCA9ICJCUCIsCiAgICAgICAgICAgICAgICAgICAgICAgYER4TUREYCA9ICJNREQiLAogICAgICAgICAgICAgICAgICAgICAgIGBzY2FsZShhZ2VEZWF0aClgID0gIkFnZSIsCiAgICAgICAgICAgICAgICAgICAgICAgYHNjYWxlKFBNSSlgID0gIlBNSSIsIAogICAgICAgICAgICAgICAgICAgICAgIGBuZXdTdHVkeU5JTUhfSEJDQ2AgPSAiTklNSF9IQkNDIiwKICAgICAgICAgICAgICAgICAgICAgICBgbmV3U3R1ZHlQaXR0YCA9ICJQaXR0IiwKICAgICAgICAgICAgICAgICAgICAgICBgbmV3U3R1ZHlNU1NNYCA9ICJNU1NNIiwKICAgICAgICAgICAgICAgICAgICAgICBgbmV3U3R1ZHlQZW5uYCA9ICJQZW5uIiwKICAgICAgICAgICAgICAgICAgICAgICBgc2NhbGUoUklOKWAgPSAiUklOIikpCgojYWdhaW4gYnV0IHN0cmF0aWZpZWQgYnkgY29ob3J0CnNuX2xtc19ieV9hZ2UgPSBzbkNUUHNfbG9uZyAlPiUgZmlsdGVyKHRvdGFsX2NlbGxzID49IDUwMCkgJT4lCiAgZ3JvdXBfYnkoY2VsbF90eXBlLCBhZ2VfZ3JvdXAsIEluc3RpdHV0aW9uKSAlPiUKICBkbyh0aWR5KGxtKHNjYWxlKHJlbF9wcm9wKSB+IHNjYWxlKFBNSSkgKyBHZW5kZXIgKyBQaGVub3R5cGUsIGRhdGEgPSAuKSkpICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUocGFkaiA9IHAuYWRqdXN0KGBwLnZhbHVlYCwgbWV0aG9kID0gJ2ZkcicpKSAlPiUKICBtdXRhdGUodGVybSA9IHJlY29kZSh0ZXJtLAogICAgICAgICAgICAgICAgICAgICAgIGAoSW50ZXJjZXB0KWAgPSAiSW50ZXJjZXB0IiwKICAgICAgICAgICAgICAgICAgICAgICBgcmVwb3J0ZWRHZW5kZXJtYWxlYCA9ICJnZW5kZXI6TWFsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgYFBoZW5vdHlwZVNaYCA9ICJTQ1oiLAogICAgICAgICAgICAgICAgICAgICAgIGBwcmltYXJ5RGlhZ25vc2lzQmlwb2xhciBEaXNvcmRlcmAgPSAiQlAiLAogICAgICAgICAgICAgICAgICAgICAgIGBEeE1ERGAgPSAiTUREIiwKICAgICAgICAgICAgICAgICAgICAgICBgc2NhbGUoYWdlRGVhdGgpYCA9ICJBZ2UiLAogICAgICAgICAgICAgICAgICAgICAgIGBzY2FsZShQTUkpYCA9ICJQTUkiLCAKICAgICAgICAgICAgICAgICAgICAgICBgbmV3U3R1ZHlOSU1IX0hCQ0NgID0gIk5JTUhfSEJDQyIsCiAgICAgICAgICAgICAgICAgICAgICAgYG5ld1N0dWR5UGl0dGAgPSAiUGl0dCIsCiAgICAgICAgICAgICAgICAgICAgICAgYG5ld1N0dWR5TVNTTWAgPSAiTVNTTSIsCiAgICAgICAgICAgICAgICAgICAgICAgYG5ld1N0dWR5UGVubmAgPSAiUGVubiIsCiAgICAgICAgICAgICAgICAgICAgICAgYHNjYWxlKFJJTilgID0gIlJJTiIpKQoKYGBgCgoKLS0tLS0tLS0tLS0tLy8vL0ZJR1VSRVMvLy8vLS0tLS0tLS0tLS0tCgpGaWd1cmUgMTogSWxsdXN0cmF0aW5nIHRoZSBhc3NvY2lhdGlvbiBiZXR3ZWVuIGNlbGwgcHJvcCBhbmQgZGlhZ25vc2lzIGFuZCBjb2hvcnQgc3BlY2lmaWMgZGlmZmVyZW5jZXMKYGBge3IsIGZpZy5oZWlnaHQ9MTMsIGZpZy53aWR0aD0xMSB9CnNpZ25pZmljYW50X2NvbWJpbmVkID0gc3Vic2V0KGNvbWJpbmVkX2xtcywgcGFkaiA8IDAuMSAmIGNlbGxfdHlwZSAlaW4lIGMoIlNTVCIsICJQVkFMQiIsICJWSVAiKSAmIHRlcm0gPT0gIlNDWiIpCnNpZ25pZmljYW50X2NvbWJpbmVkJGFzdGVyaXNrcyA8LSBpZmVsc2Uoc2lnbmlmaWNhbnRfY29tYmluZWQkcGFkaiA8PSAwLjAxLCAiKioqIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2Uoc2lnbmlmaWNhbnRfY29tYmluZWQkcGFkaiA8PSAwLjA1LCAiKioiLCAiKiIpKQpzaWduaWZpY2FudF9jb21iaW5lZCR2anVzdCA8LSBpZmVsc2Uoc2lnbmlmaWNhbnRfY29tYmluZWQkZXN0aW1hdGUgPj0gMCwgLTMsIDcpCgpmaWd1cmVfMWIgPSBjb21iaW5lZF9sbXMgJT4lIAogIGZpbHRlcih0ZXJtID09ICdTQ1onLCBjZWxsX3R5cGUgJWluJSBjKCJTU1QiLCAiUFZBTEIiLCAiVklQIikpICU+JSAKICBtdXRhdGUoY2VsbF90eXBlID0gZmN0X3Jlb3JkZXIoY2VsbF90eXBlLCBlc3RpbWF0ZSkpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBuZXdTdHVkeSwgeSA9IGVzdGltYXRlLCBmaWxsID0gbmV3U3R1ZHkpKSArIAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDApICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKyAKICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gZXN0aW1hdGUgLSBzdGQuZXJyb3IsIHltYXggPSBlc3RpbWF0ZSArIHN0ZC5lcnJvcikpICsgCiAgeWxhYignQmV0YSBDb2VmZmljaWVudCcpICsgCiAgeGxhYignJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGNiUGFsZXR0ZSkgKwogIGdlb21fdGV4dChkYXRhID0gc2lnbmlmaWNhbnRfY29tYmluZWQsCiAgICAgICAgICAgIGFlcyhuZXdTdHVkeSwgZXN0aW1hdGUsIGxhYmVsID0gYXN0ZXJpc2tzKSwgCiAgICAgICAgICAgIHZqdXN0ID0gc2lnbmlmaWNhbnRfY29tYmluZWQkdmp1c3QpICsKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dCh2anVzdCA9IDYsIHNpemUgPSAxNSksCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSwKICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgYXhpcy5saW5lLnggPSBlbGVtZW50X2JsYW5rKCksIAogICAgYXhpcy50aWNrcy54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgZmFjZXRfZ3JpZCh+Y2VsbF90eXBlLCBkcm9wID0gVCwgc2NhbGUgPSAiZnJlZV94Iiwgc3BhY2UgPSAiZnJlZSIpICsKICBndWlkZXMoZmlsbCA9IGd1aWRlX25vbmUoKSkgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKC0wLjksIDAuNSkpCgojIEZpZ3VyZSAxYzogUmVzaWR1YWxpemVkIE1HUHMgdnMuIERpYWdub3NpcwpmaWd1cmVfMWMgPC0gbWdwX2VzdGltYXRpb25zX2xvbmcgJT4lCiAgZmlsdGVyKGNlbGxfdHlwZSAlaW4lIGMoIlNTVCIsICJQVkFMQiIsICJWSVAiKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gcHJpbWFyeURpYWdub3NpcywgeSA9IHJlc2lkKSkgKwogIGdlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlID0gTkEsIGFlcyhmaWxsID0gbmV3U3R1ZHksIGFscGhhID0gMC41KSwgc2hvdy5sZWdlbmQgPSBGLCBub3RjaCA9IFQpICsKICBnZW9tX2JlZXN3YXJtKHNpemUgPSAxLCBhbHBoYSA9IDAuMywgYWVzKGNvbG91ciA9IG5ld1N0dWR5KSwgc2hvdy5sZWdlbmQgPSBGKSArCiAgeWxhYignUmVzaWR1YWxpemVkIENUUHMnKSArIAogIHhsYWIoJ0RpYWdub3NpcycpICsgCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBjKCJDIiwgIlNDWiIpKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjYlBhbGV0dGUpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjYlBhbGV0dGUpICsKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHZqdXN0ID0gLTIsIHNpemUgPSAxNSksCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwKICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCkpICsKICBndWlkZXMoZmlsbCA9IEZBTFNFKSArCiAgZmFjZXRfZ3JpZChyb3dzID0gdmFycyhjZWxsX3R5cGUpLCBjb2xzID0gdmFycyhuZXdTdHVkeSksIGRyb3AgPSBULCBzY2FsZSA9ICJmcmVlIiwgc3dpdGNoID0gInkiLCBzcGFjZSA9ICJmcmVlIikgKwogIGdlb21fc2lnbmlmKGNvbXBhcmlzb25zID0gbGlzdChjKCJjb250cm9sIiwgIlNjaGl6b3BocmVuaWEiKSksIG1hcF9zaWduaWZfbGV2ZWwgPSBUUlVFLCAsIHRlc3QgPSB3aWxjb3gudGVzdCkgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKC0zLjUsIDMuNSkpIAoKI0ZpZ3VyZSAxZCAtIEhpc290Z3JhbSBvZiBjb2hvcnQgYWdlcwpmaWd1cmVfMWQgPSBtZ3BfZXN0aW1hdGlvbnMgJT4lIAogICAgZ2dwbG90KGFlcyh4ID0gYWdlRGVhdGgpKSArCiAgICBnZW9tX2hpc3RvZ3JhbShhZXMoZmlsbCA9IG5ld1N0dWR5KSkgKwogICAgZmFjZXRfd3JhcCh+IG5ld1N0dWR5LCBucm93ID0gNiwgc2NhbGVzID0gImZyZWVfeSIpICsKICAgIGxhYnMoeCA9ICJhZ2VEZWF0aCIsIHkgPSAiRnJlcXVlbmN5IikgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiIzU2QjRFOSIsICIjMDA5RTczIiwiI0U2OUYwMCIsICIjMDA3MkIyIiwgIiNENTVFMDAiLCAiI0NDNzlBNyIpKSArIAogIHNjYWxlX3lfY29udGludW91cyhuLmJyZWFrcyA9IDQpICsKICBndWlkZXMoZmlsbCA9IGd1aWRlX25vbmUoKSkgKyAKICB0aGVtZShwYW5lbC5ncmlkID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgc3RyaXAudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gcmVsKDAuOSksIGZhY2UgPSAicGxhaW4iKSwKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMywpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEzKSwKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dCggc2l6ZSA9IDE1KSwKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpLAogICAgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCkpCgojIENvbWJpbmUgRmlndXJlIDEKZmlndXJlXzFfdG9wID0gKGZpZ3VyZV8xYiB8IGZpZ3VyZV8xZCkgKyBwbG90X2xheW91dCh3aWR0aHMgPSBjKDQsMikpCmZpZ3VyZV8xX2JvdHRvbSA9IGZpZ3VyZV8xYwogIApmaWd1cmVfMSA9ICBmaWd1cmVfMV90b3AgLyBmaWd1cmVfMV9ib3R0b20gKyBwbG90X2Fubm90YXRpb24odGFnX2xldmVscyA9ICdBJykgKyBwbG90X2xheW91dChoZWlnaHRzID0gYygxLCAxKSkgJiB0aGVtZShwbG90LnRhZyA9IGVsZW1lbnRfdGV4dChzaXplID0gMjUpKQpmaWd1cmVfMQoKI1NhdmUgdG8gcGxvdHMgZm9sZGVyCm91dHB1dF9maWxlbmFtZSA8LSAifi9jZWxsX3Byb3BfcHN5Y2hpYXRyeS9wbG90cy9maWd1cmVfMS5qcGciCgojIFVzZSBnZ3NhdmUgdG8gc2F2ZSB0aGUgcGxvdCBhcyBhIEpQRyBpbWFnZQpnZ3NhdmUob3V0cHV0X2ZpbGVuYW1lLCBmaWd1cmVfMSwgd2lkdGggPSAxMSwgaGVpZ2h0ID0gMTMsIGRwaSA9IDUwMCkKCmBgYAoKRmlndXJlIDI6IEludHJvZHVjaW5nIGFuZCBwcmVzZW50aW5nIGFuIGludGVyYWN0aW9uIGJldHdlZW4gYWdlIGFuZCBkaWFnbm9zaXMgdy9ydCBjZWxsIHByb3BvcnRpb24uCmBgYHtyLCBmaWcuaGVpZ2h0PTEzLCBmaWcud2lkdGg9MTN9CiMgUGxvdCBpbnRlcmFjdGlvbiBiZXRhIHZhbHVlcyBmcm9tIG1lZ2FuYWx5c2lzCnNpZ25pZmljYW50X21lZ2EgPSBzdWJzZXQobWVnYV9sbXMsIHBhZGogPD0gMC4xICYgdGVybSA9PSAiQWdlIHggRGlhZ25vc2lzIiAmIGNlbGxfdHlwZSAlaW4lIGMoIkFzdHJvY3l0ZSIsICJJVCIsICJMNS42Lk5QIiwgIkxBTVA1IiwgIk1pY3JvZ2xpYSIsICJPbGlnb2RlbmRyb2N5dGUiLCAiT1BDIiwgIlBlcmljeXRlIiwgIlBWQUxCIiwgIlNTVCIsICJWSVAiICkpCnNpZ25pZmljYW50X21lZ2EkYXN0ZXJpc2tzID0gaWZlbHNlKHNpZ25pZmljYW50X21lZ2EkcGFkaiA8PSAwLjAxLCAiKioqIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2Uoc2lnbmlmaWNhbnRfbWVnYSRwYWRqIDw9IDAuMDUsICIqKiIsICIqIikpCnNpZ25pZmljYW50X21lZ2Ekdmp1c3QgPSBpZmVsc2Uoc2lnbmlmaWNhbnRfbWVnYSRlc3RpbWF0ZSA+PSAwLCAtMywgMykKCiMgRmlndXJlIDJhOiBSZXN1bHRzIGZyb20gaW50ZXJhY3Rpb24gbWVnYW5hbHlzaXMgc3RyYXRpZmllZCBieSBjZWxsIGNsYXNzIC0gcGxvdCBpbnRlcmFjdGlvbiBiZXRhIGNvZWZmCm1lZ2FfbG1zJGNlbGxfdHlwZSA9IG1lZ2FfbG1zJGNlbGxfdHlwZSAlPiUgZmFjdG9yKGxldmVscyA9IGMoIlBWQUxCIiwgIlNTVCIsICJWSVAiLCAiTEFNUDUiLCAiUEFYNiIsICJJVCIsICJMNC5JVCIsICJMNS5FVCIsICJMNS42LklULkNhcjMiLCAiTDUuNi5OUCIsICJMNi5DVCIsICJMNmIiLCAiQXN0cm9jeXRlIiwgIkVuZG90aGVsaWFsIiwgIk1pY3JvZ2xpYSIsICJPbGlnb2RlbmRyb2N5dGUiLCAiT1BDIiwgIlBlcmljeXRlIiwgIlZMTUMiKSkKZmlndXJlXzJhID0gbWVnYV9sbXMgJT4lICBmaWx0ZXIoY2VsbF90eXBlICVpbiUgYygiQXN0cm9jeXRlIiwgIklUIiwgIkw1LjYuTlAiLCAiTEFNUDUiLCAiTWljcm9nbGlhIiwgIk9saWdvZGVuZHJvY3l0ZSIsICJPUEMiLCJQZXJpY3l0ZSIsICJQVkFMQiIsICJTU1QiLCAiVklQIiApKSAlPiUKICBmaWx0ZXIodGVybSAlaW4lICdBZ2UgeCBEaWFnbm9zaXMnKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gY2VsbF90eXBlLCB5ID0gZXN0aW1hdGUpKSArIAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDApICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKyAKICBmYWNldF9ncmlkKH5jZWxsX2NsYXNzLCBkcm9wID0gVCwgc2NhbGUgPSAiZnJlZV94Iiwgc3BhY2UgPSAiZnJlZSIpICsKICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gZXN0aW1hdGUgLSBzdGQuZXJyb3IsIHltYXggPSBlc3RpbWF0ZSArIHN0ZC5lcnJvcikpICsgCiAgeWxhYignSW50ZXJhY3Rpb24gQ29lZmZpY2llbnQnKSArIAogIHhsYWIoJ0NlbGwgVHlwZScpICsgCiAgZ2VvbV90ZXh0KGRhdGEgPSBzaWduaWZpY2FudF9tZWdhLAogICAgICAgICAgICBhZXMoY2VsbF90eXBlLCBlc3RpbWF0ZSwgbGFiZWwgPSBhc3Rlcmlza3MpLCAKICAgICAgICAgICAgdmp1c3QgPSBzaWduaWZpY2FudF9tZWdhJHZqdXN0KSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCB2anVzdCA9IDEsIGhqdXN0ID0gMSwgc2l6ZSA9IDEyKSwgCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHZqdXN0ID0gOCwgc2l6ZSA9IDE1KSwKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLAogICAgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCkpICsKICBndWlkZXMoZmlsbCA9IEZBTFNFKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoLTAuMTUsIDAuMzIpKQoKIyBGaWd1cmUgMmI6IFBsb3QgYmV0YSB2YWx1ZXMgZnJvbSBtZWdhbmFseXNpcyBpbiAxMC15ZWFyIGFnZSBiaW5zIGZvciBTU1QsIFBWQUxCLCBWSVAKc2lnbmlmaWNhbnRfbWVnYV9hZ2UgPSBzdWJzZXQobWVnYV9sbXNfYnlfYWdlLCBwYWRqIDw9IDAuMSAmIHRlcm0gPT0gIlNDWiIgJiBjZWxsX3R5cGUgJWluJSBjKCJTU1QiLCAiUFZBTEIiLCAiVklQIikpCnNpZ25pZmljYW50X21lZ2FfYWdlJGFzdGVyaXNrcyA9IGlmZWxzZShzaWduaWZpY2FudF9tZWdhX2FnZSRwYWRqIDw9IDAuMDEsICIqKioiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShzaWduaWZpY2FudF9tZWdhX2FnZSRwYWRqIDw9IDAuMDUsICIqKiIsICIqIikpCnNpZ25pZmljYW50X21lZ2FfYWdlJHZqdXN0ID0gaWZlbHNlKHNpZ25pZmljYW50X21lZ2FfYWdlJGVzdGltYXRlID49IDAsIC0yLCAzKQoKZmlndXJlXzJiID0gbWVnYV9sbXNfYnlfYWdlICU+JSAKICBmaWx0ZXIodGVybSAlaW4lICdTQ1onICYgY2VsbF90eXBlICVpbiUgYygiU1NUIiwgIlBWQUxCIiwgIlZJUCIpKSAlPiUgCiAgbXV0YXRlKGNlbGxfdHlwZSA9IGZjdF9yZW9yZGVyKGNlbGxfdHlwZSwgZXN0aW1hdGUpKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gYWdlX3JhbmdlLCB5ID0gZXN0aW1hdGUpKSArIAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDApICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKyAKICBnZW9tX2Vycm9yYmFyKGFlcyh5bWluID0gZXN0aW1hdGUgLSBzdGQuZXJyb3IsIHltYXggPSBlc3RpbWF0ZSArIHN0ZC5lcnJvcikpICsgCiAgeWxhYignQmV0YSBDb2VmZmljaWVudCcpICsgCiAgeGxhYignXG4gXG4gQWdlIFJhbmdlJykgKyAKICBnZW9tX3RleHQoZGF0YSA9IHNpZ25pZmljYW50X21lZ2FfYWdlLAogICAgICAgICAgICBhZXMoYWdlX3JhbmdlLCBlc3RpbWF0ZSwgbGFiZWwgPSBhc3Rlcmlza3MpLCAKICAgICAgICAgICAgdmp1c3QgPSBzaWduaWZpY2FudF9tZWdhX2FnZSR2anVzdCkgKwogICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIHZqdXN0ID0gMSwgaGp1c3QgPSAxLCBzaXplID0gMTMpLCAKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQodmp1c3QgPSA4LCBzaXplID0gMTUpLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksCiAgICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSkgKwogIGZhY2V0X3dyYXAofmNlbGxfdHlwZSwgZHJvcCA9IFQsIHNjYWxlID0gImZyZWVfeCIpICsKICBndWlkZXMoZmlsbCA9IEZBTFNFKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoLTEuMCwgMS40KSkKCgojIEZpZ3VyZSAyYzogVmlzdWFsaXppbmcgaW50ZXJhY3Rpb24gYmV0d2VlbiBhZ2UgYW5kIGRpYWdub3NpcyBieSBwbG90dGluZyByZWxfcHJvcCB2cy4gYWdlIGluIGVhY2ggZGlhZ25vc2lzIApmaWd1cmVfMmMgPSBtZ3BfZXN0aW1hdGlvbnNfbG9uZyAlPiUgZmlsdGVyKGFnZURlYXRoIDwgOTApICU+JQogIGZpbHRlcihjZWxsX3R5cGUgJWluJSBjKCJTU1QiLCAiUFZBTEIiLCAiVklQIikpICU+JQogIGdncGxvdChhZXMoeCA9IGFnZURlYXRoLCB5ID0gcmVsX3Byb3AsIGNvbG9yID0gcHJpbWFyeURpYWdub3NpcykpICsKICBnZW9tX3BvaW50KGFscGhhID0gMC41LCBzaXplID0gMC41KSArIAogIGdlb21fc21vb3RoKHNlID0gRiwgbWV0aG9kID0gJ2xtJywgZnVsbHJhbmdlID0gVCkgKwogIHlsYWIoJ1JlbGF0aXZlIENlbGwgUHJvcC4nKSArIAogIHhsYWIoJ0FnZScpICsgCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dCh2anVzdCA9IC0yLCBzaXplID0gMTUpLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksCiAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuOSwgMC45KSwKICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksICAKICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpKSArCiAgZmFjZXRfZ3JpZCh+Y2VsbF90eXBlLCBkcm9wID0gVCwgc2NhbGUgPSAiZnJlZV94Iiwgc3BhY2UgPSAiZnJlZSIpICsKICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChucm93ID0gMSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygtMywgMykpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImRvZGdlcmJsdWUyIiwgImZpcmVicmljazIiKSwgbGFiZWxzPWMoJ0NPTicsICdTQ1onKSkKCiNGaWd1cmUgMmQ6IE1lZ2EtYW5hbHlzaXMgcmVzdWx0cyB3aGVuIGJpbm5lZCBhcyArLy0gNzAgeWVhcnMKc2lnbmlmaWNhbnRfbWVnYV9hZ2VfY2xhc3MgPSBzdWJzZXQobWVnYV9sbXNfYnlfYWdlX2NsYXNzLCBwYWRqIDw9IDAuMSAmIHRlcm0gPT0gIlNDWiIgJiBjZWxsX3R5cGUgJWluJSBjKCJTU1QiLCAiUFZBTEIiLCAiVklQIikpCnNpZ25pZmljYW50X21lZ2FfYWdlX2NsYXNzJGFzdGVyaXNrcyA9IGlmZWxzZShzaWduaWZpY2FudF9tZWdhX2FnZV9jbGFzcyRwYWRqIDw9IDAuMDEsICIqKioiLCBpZmVsc2Uoc2lnbmlmaWNhbnRfbWVnYV9hZ2VfY2xhc3MkcGFkaiA8PSAwLjA1LCAiKioiLCAiKiIpKQpzaWduaWZpY2FudF9tZWdhX2FnZV9jbGFzcyR2anVzdCA9IGlmZWxzZShzaWduaWZpY2FudF9tZWdhX2FnZV9jbGFzcyRlc3RpbWF0ZSA+PSAwLCAtMywgMykKCmZpZ3VyZV8yZCA9IG1lZ2FfbG1zX2J5X2FnZV9jbGFzcyAlPiUgCiAgZmlsdGVyKHRlcm0gJWluJSAnU0NaJyAmIGNlbGxfdHlwZSAlaW4lIGMoIlNTVCIsICJQVkFMQiIsICJWSVAiKSkgJT4lIAogIG11dGF0ZShjZWxsX3R5cGUgPSBmY3RfcmVvcmRlcihjZWxsX3R5cGUsIGVzdGltYXRlKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IGFnZV9jbGFzcywgeSA9IGVzdGltYXRlLCBmaWxsID0gYWdlX2NsYXNzKSkgKyAKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwKSArIAogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsgCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbiA9IGVzdGltYXRlIC0gc3RkLmVycm9yLCB5bWF4ID0gZXN0aW1hdGUgKyBzdGQuZXJyb3IpKSArIAogIHlsYWIoJ0JldGEgQ29lZmZpY2llbnQnKSArIAogIHhsYWIoJ1xuIFxuIEFnZSBSYW5nZScpICsgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygibWVkaXVtcHVycGxlMyIsICJvbGl2ZWRyYWIzIiksIGxhYmVscz1jKCdBYm92ZSA3MCcsICdCZWxvdyA3MCcpKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBzaWduaWZpY2FudF9tZWdhX2FnZV9jbGFzcywKICAgICAgICAgICAgYWVzKGFnZV9jbGFzcywgZXN0aW1hdGUsIGxhYmVsID0gYXN0ZXJpc2tzKSwgCiAgICAgICAgICAgIHZqdXN0ID0gc2lnbmlmaWNhbnRfbWVnYV9hZ2VfY2xhc3Mkdmp1c3QpICsKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIHZqdXN0ID0gMSwgaGp1c3QgPSAxLCBzaXplID0gMTMpLCAKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQodmp1c3QgPSA4LCBzaXplID0gMTUpLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksCiAgICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSkgKwogIGZhY2V0X3dyYXAofmNlbGxfdHlwZSwgZHJvcCA9IFQsIHNjYWxlID0gImZyZWVfeCIpICsKICBndWlkZXMoZmlsbCA9IEZBTFNFKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoLTAuNTUsIDAuNTUpKQoKZmlndXJlXzJfbWlkZGxlID0gKGZpZ3VyZV8yYiArZmlndXJlXzJkKSArIHBsb3RfbGF5b3V0KHdpZHRocyA9IGMoMiwxKSkKZmlndXJlXzIgPSAgZmlndXJlXzJjIC8gZmlndXJlXzJfbWlkZGxlIC8gZmlndXJlXzJhCmZpZ3VyZV8yID0gZmlndXJlXzIgKyBwbG90X2Fubm90YXRpb24odGFnX2xldmVscyA9ICdBJykgKyBwbG90X2xheW91dChoZWlnaHRzID0gYygxLCAxLCAxKSkgJiB0aGVtZShwbG90LnRhZyA9IGVsZW1lbnRfdGV4dChzaXplID0gMjUpKQpmaWd1cmVfMgoKI1NhdmUgdG8gcGxvdHMgZm9sZGVyCm91dHB1dF9maWxlbmFtZSA8LSAifi9jZWxsX3Byb3BfcHN5Y2hpYXRyeS9wbG90cy9maWd1cmVfMi5qcGciCgojIFVzZSBnZ3NhdmUgdG8gc2F2ZSB0aGUgcGxvdCBhcyBhIEpQRyBpbWFnZQpnZ3NhdmUob3V0cHV0X2ZpbGVuYW1lLCBmaWd1cmVfMiwgd2lkdGggPSAxMywgaGVpZ2h0ID0gMTUsIGRwaSA9IDUwMCkKCmBgYAoKRmlndXJlIDM6IFZhbGlkYXRpbmcgdHJlbmRzIGFuZCBpbnRlcmFjdGlvbnMgaW4gc3NSTkFzZXEgCmBgYHtyLCBmaWcuaGVpZ2h0PSAxMiwgZmlnLndpZHRoPTEyfQojIEZpZ3VyZSAzYTogc25STkFzZXEgY2VsbCBwcm9wb3J0aW9ucyBiaW5uZWQgYnkgYWdlCnNpZ25pZmljYW50X3NuX21lZ2FfYnlfYWdlIDwtIHN1YnNldChzbl9tZWdhX2xtc19ieV9hZ2UsIHBhZGogPD0gMC4xICYgdGVybSA9PSAiU0NaIiAmIGNlbGxfdHlwZSAlaW4lIGMoIlNTVCIsICJQVkFMQiIsICJWSVAiKSkKc2lnbmlmaWNhbnRfc25fbWVnYV9ieV9hZ2UkYXN0ZXJpc2tzIDwtIGlmZWxzZShzaWduaWZpY2FudF9zbl9tZWdhX2J5X2FnZSRwYWRqIDw9IDAuMDEsICIqKioiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShzaWduaWZpY2FudF9zbl9tZWdhX2J5X2FnZSRwYWRqIDw9IDAuMDUsICIqKiIsICIqIikpCnNpZ25pZmljYW50X3NuX21lZ2FfYnlfYWdlJHZqdXN0IDwtIGlmZWxzZShzaWduaWZpY2FudF9zbl9tZWdhX2J5X2FnZSRlc3RpbWF0ZSA+PSAwLCAtNSwgNSkKCmZpZ3VyZV8zYSA8LSBzbl9tZWdhX2xtc19ieV9hZ2UgJT4lCiAgZmlsdGVyKHRlcm0gJWluJSAnU0NaJyAmIGNlbGxfdHlwZSAlaW4lIGMoIlNTVCIsICJQVkFMQiIsICJWSVAiKSkgJT4lCiAgbXV0YXRlKGNlbGxfdHlwZSA9IGZjdF9yZW9yZGVyKGNlbGxfdHlwZSwgZXN0aW1hdGUpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBhZ2VfZ3JvdXAsIHkgPSBlc3RpbWF0ZSwgZmlsbCA9IGFnZV9ncm91cCkpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKwogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBlc3RpbWF0ZSAtIHN0ZC5lcnJvciwgeW1heCA9IGVzdGltYXRlICsgc3RkLmVycm9yKSkgKwogIHlsYWIoJ0JldGEgQ29lZmZpY2llbnQnKSArCiAgeGxhYignQWdlIFJhbmdlIChBYm92ZS9CZWxvdyA3MCknKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygibWVkaXVtcHVycGxlMyIsICJvbGl2ZWRyYWIzIiksIGxhYmVscyA9IGMoJ0Fib3ZlIDcwJywgJ0JlbG93IDcwJykpICsKICBnZW9tX3RleHQoZGF0YSA9IHNpZ25pZmljYW50X3NuX21lZ2FfYnlfYWdlLAogICAgICAgICAgICBhZXMoYWdlX2dyb3VwLCBlc3RpbWF0ZSwgbGFiZWwgPSBhc3Rlcmlza3MpLAogICAgICAgICAgICB2anVzdCA9IHNpZ25pZmljYW50X3NuX21lZ2FfYnlfYWdlJHZqdXN0KSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCB2anVzdCA9IDEsIGhqdXN0ID0gMSwgc2l6ZSA9IDEzKSwKICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksCiAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQodmp1c3QgPSAxLCBzaXplID0gMTUpLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksCiAgICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKC0xLCAxKSkgKwogIGZhY2V0X3dyYXAofmNlbGxfdHlwZSwgZHJvcCA9IFRSVUUsIHNjYWxlID0gImZyZWUiKQoKIyBGaWd1cmUgM2I6IFJlbGF0aXZlIGNlbGwgcHJvcG9ydGlvbnMgZnJvbSBzbkNUUHMKZmlndXJlXzNiIDwtIHNuQ1RQc19sb25nICU+JSBmaWx0ZXIodG90YWxfY2VsbHMgPj0gNTAwKSAlPiUKICBmaWx0ZXIoUGhlbm90eXBlICVpbiUgYygnU1onLCAiQ09OIiksIGNlbGxfdHlwZSAlaW4lIGMoIlNTVCIsICJQVkFMQiIsICJWSVAiKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gQWdlLCB5ID0gcmVsX3Byb3AsIGNvbG9yID0gUGhlbm90eXBlKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjgsIHNpemUgPSAxLjMpICsgCiAgZ2VvbV9zbW9vdGgoc2UgPSBGLCBtZXRob2QgPSAnbG0nKSArCiAgc2NhbGVfY29sb3VyX2h1ZShsYWJlbHMgPSBjKCJDb250cm9sIiwgIlNDWiIpKSArCiAgeWxhYignUmVsYXRpdmUgQ2VsbCBQcm9wLicpICsgCiAgeGxhYignQWdlJykgKyAKICB0aGVtZSgKICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMyksCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTMpLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KCwgc2l6ZSA9IDE2KSwKICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gYygwLjk0LCAwLjk0KSwKICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksIAogICAgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksCiAgICBzdHJpcC50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KQogICkgKwogIGZhY2V0X3JlcF9ncmlkKHJvd3MgPSB2YXJzKEluc3RpdHV0aW9uKSwKICAgIGNvbHMgPSB2YXJzKGNlbGxfdHlwZSksCiAgICBkcm9wID0gVCwKICAgIHNjYWxlID0gImZyZWUiLAogICAgc3dpdGNoID0gInkiLAogICAgc3BhY2UgPSAiZnJlZV94IiwKICAgIHJlcGVhdC50aWNrLmxhYmVscyA9IFQKICApICsKICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZChucm93ID0gMSkpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImRvZGdlcmJsdWUyIiwgImZpcmVicmljazIiKSwgbGFiZWxzPWMoJ0NPTicsICdTQ1onKSkKCiMgRmlndXJlIDNjOiBJbnRlcmFjdGlvbiB0ZXJtcyBmb3Igc25STkFzZXEgcmVzdWx0cwpzaWduaWZpY2FudF9zbl9tZWdhIDwtIHN1YnNldChzbl9tZWdhX2xtcywgcGFkaiA8PSAwLjEgJiB0ZXJtID09ICJJbnRlcmFjdGlvbiIgJiBjZWxsX3R5cGUgJWluJSBjKCJBc3Ryb2N5dGUiLCAiSVQiLCAiTDUuNi5OUCIsICJMQU1QNSIsICJNaWNyb2dsaWEiLCAiT2xpZ29kZW5kcm9jeXRlIiwgIk9QQyIsIlBlcmljeXRlIiwgIlBWQUxCIiwgIlNTVCIsICJWSVAiICkpCnNpZ25pZmljYW50X3NuX21lZ2EkYXN0ZXJpc2tzIDwtIGlmZWxzZShzaWduaWZpY2FudF9zbl9tZWdhJHBhZGogPD0gMC4wMSwgIioqKiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHNpZ25pZmljYW50X3NuX21lZ2EkcGFkaiA8PSAwLjA1LCAiKioiLCAiKiIpKQpzaWduaWZpY2FudF9zbl9tZWdhJHZqdXN0IDwtIGlmZWxzZShzaWduaWZpY2FudF9zbl9tZWdhJGVzdGltYXRlID49IDAsIC03LCA3KQoKc25fbWVnYV9sbXMkY2VsbF90eXBlID0gc25fbWVnYV9sbXMkY2VsbF90eXBlICU+JSBmYWN0b3IobGV2ZWxzID0gYygiUFZBTEIiLCAiU1NUIiwgIlZJUCIsICJMQU1QNSIsICJQQVg2IiwgIklUIiwgIkw0LklUIiwgIkw1LkVUIiwgIkw1LjYuSVQuQ2FyMyIsICJMNS42Lk5QIiwgIkw2LkNUIiwgIkw2YiIsICJBc3Ryb2N5dGUiLCAiRW5kb3RoZWxpYWwiLCAiTWljcm9nbGlhIiwgIk9saWdvZGVuZHJvY3l0ZSIsICJPUEMiLCAiUGVyaWN5dGUiLCAiVkxNQyIpKQpmaWd1cmVfM2MgPSBzbl9tZWdhX2xtcyAlPiUgZmlsdGVyKGNlbGxfdHlwZSAlaW4lIGMoIkFzdHJvY3l0ZSIsICJJVCIsICJMNS42Lk5QIiwgIkxBTVA1IiwgIk1pY3JvZ2xpYSIsICJQZXJpY3l0ZSIsICJQVkFMQiIsICJTU1QiLCAiVklQIiApKSAlPiUKICBmaWx0ZXIodGVybSAlaW4lICdJbnRlcmFjdGlvbicpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBjZWxsX3R5cGUsIHkgPSBlc3RpbWF0ZSkpICsgCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMCkgKyAKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbiA9IGVzdGltYXRlIC0gc3RkLmVycm9yLCB5bWF4ID0gZXN0aW1hdGUgKyBzdGQuZXJyb3IpKSArIAogICAgZmFjZXRfZ3JpZCh+Y2VsbF9jbGFzcywgZHJvcCA9IFQsIHNjYWxlID0gImZyZWVfeCIsIHNwYWNlID0gImZyZWUiKSArCiAgeWxhYignSW50ZXJhY3Rpb24gQ29lZmZpY2llbnQnKSArIAogIHhsYWIoJ0NlbGwgVHlwZScpICsgCiAgZ2VvbV90ZXh0KGRhdGEgPSBzaWduaWZpY2FudF9zbl9tZWdhLAogICAgICAgICAgICBhZXMoY2VsbF90eXBlLCBlc3RpbWF0ZSwgbGFiZWwgPSBhc3Rlcmlza3MpLCAKICAgICAgICAgICAgdmp1c3QgPSBzaWduaWZpY2FudF9zbl9tZWdhJHZqdXN0KSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCB2anVzdCA9IDEsIGhqdXN0ID0gMSwgc2l6ZSA9IDEyKSwgCiAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHZqdXN0ID0gLTIsIHNpemUgPSAxNSksCiAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwKICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKSArCiAgZ3VpZGVzKGZpbGwgPSBGQUxTRSkgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKC0wLjQ1LCAwLjY1KSkKCgojIEZpZ3VyZSAzOiBDb21iaW5lZCBmaWd1cmUKZmlndXJlXzNfdG9wID0gZmlndXJlXzNiCmZpZ3VyZV8zX2JvdHRvbSAgPSBmaWd1cmVfM2MgKyBmaWd1cmVfM2EgKyBwbG90X2xheW91dCh3aWR0aHMgPSBjKDIsMSkpIApmaWd1cmVfMyA8LSBmaWd1cmVfM190b3AvZmlndXJlXzNfYm90dG9tCmZpZ3VyZV8zID0gZmlndXJlXzMgKyBwbG90X2Fubm90YXRpb24odGFnX2xldmVscyA9ICdBJykgKyBwbG90X2xheW91dChoZWlnaHRzID0gYygyLDEpKSAmIHRoZW1lKHBsb3QudGFnID0gZWxlbWVudF90ZXh0KHNpemUgPSAyNSkpCmZpZ3VyZV8zCgojU2F2ZSB0byBwbG90cyBmb2xkZXIKb3V0cHV0X2ZpbGVuYW1lIDwtICJ+L2NlbGxfcHJvcF9wc3ljaGlhdHJ5L3Bsb3RzL2ZpZ3VyZV8zLmpwZyIKCiMgVXNlIGdnc2F2ZSB0byBzYXZlIHRoZSBwbG90IGFzIGEgSlBHIGltYWdlCmdnc2F2ZShvdXRwdXRfZmlsZW5hbWUsIGZpZ3VyZV8zLCB3aWR0aCA9IDEzLCBoZWlnaHQgPSAxNiwgZHBpID0gNTAwKQoKYGBgCgpGaWd1cmUgNTogQWdlT25zZXQgdnMuIE1HUApgYGB7ciwgZmlnLndpZHRoPTksIGZpZy5oZWlnaHQ9Nn0KI0R1cmF0aW9uIG9mIElsbG5lc3MKbWdwX2VzdGltYXRpb25zX2xvbmckZHVyYXRpb25JbGxuZXNzIDwtIG1ncF9lc3RpbWF0aW9uc19sb25nJGFnZURlYXRoIC0gbWdwX2VzdGltYXRpb25zX2xvbmckYWdlT25zZXQKCkNFTExfVFlQRVMgPSBjKCJTU1QiLCAiUFZBTEIiLCAiVklQIikKREFUQVNFVFMgPSBjKCJHVkVYIiwgIkxJQkRfc3pDb250cm9sIikKYWdlT25zZXRfcmVzaWR1YWxzX2RhdGEgPSBtYXRyaXgobmNvbCA9IChuY29sKG1ncF9lc3RpbWF0aW9uc19sb25nKSsxKSwgbnJvdyA9IDApICU+JSBkYXRhLmZyYW1lKCkKY29sbmFtZXMoYWdlT25zZXRfcmVzaWR1YWxzX2RhdGEpID0gYyhjb2xuYW1lcyhtZ3BfZXN0aW1hdGlvbnNfbG9uZyksICJyZXNpZCIpCgpmb3IoREFUQVNFVCBpbiBEQVRBU0VUUykgewogIGZvcihDRUxMX1RZUEUgaW4gQ0VMTF9UWVBFUykgewogICAgI0NyZWF0ZSByZXNfbW9kZWwgd2l0aG91dCBhZ2VPbnNldCB2YXJpYWJsZQogICAgcmVzX21vZGVsX2RhdGEgPSBtZ3BfZXN0aW1hdGlvbnNfbG9uZyAlPiUgZmlsdGVyKHByaW1hcnlEaWFnbm9zaXMgJWluJSBjKCdTY2hpem9waHJlbmlhJyksIGNlbGxfdHlwZSA9PSBDRUxMX1RZUEUgLCBkYXRhc2V0ID09IERBVEFTRVQsIGlzLm5hKGFnZU9uc2V0KSA9PSBGKQogICAgI01vZGVsIHdpdGhvdXQgYWdlT25zZXQKICAgIHJlc19tb2RlbCA9IGxtKHNjYWxlKHJlbF9wcm9wKSB+IHNjYWxlKGFnZURlYXRoKSArIHNjYWxlKFJJTikgKyBzY2FsZShQTUkpICsgcmVwb3J0ZWRHZW5kZXIsIGRhdGEgPSByZXNfbW9kZWxfZGF0YSkKICAgICNDYWxjdWxhdGUgcmVzaWR1YWxzIGFuZCBhZGQgY29sdW1uIHRvIHJlc19tb2RlbF9kYXRhCiAgICByZXNfbW9kZWxfZGF0YSA9IHJlc19tb2RlbF9kYXRhICU+JSBhZGRfcmVzaWR1YWxzKHZhciA9ICJyZXNpZCIsIG1vZGVsID0gcmVzX21vZGVsKQogICAgYWdlT25zZXRfcmVzaWR1YWxzX2RhdGEgPSBhZ2VPbnNldF9yZXNpZHVhbHNfZGF0YSAlPiUgcmJpbmQocmVzX21vZGVsX2RhdGEpCiAgfQp9CgpmaWd1cmVfNSA8LSBhZ2VPbnNldF9yZXNpZHVhbHNfZGF0YSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBhZ2VPbnNldCwgeSA9IHJlc2lkLCBjb2xvciA9IGRhdGFzZXQpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNSwgc2l6ZSA9IDAuOCkgKyAKICBnZW9tX3Ntb290aChzZSA9IEZBTFNFLCBtZXRob2QgPSAnbG0nLCBmdWxscmFuZ2UgPSBUUlVFKSArCiAgeWxhYignckNUUCByZXNpZHVhbHMgKEFVKScpICsgCiAgeGxhYignQWdlIG9mIFNDWiBvbnNldCcpICsgCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpLAogICAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSwKICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dCh2anVzdCA9IC0yLCBzaXplID0gMTYsIG1hcmdpbiA9IG1hcmdpbih0ID0gOCkpLAogICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiksCiAgICBzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNiksCiAgICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYpLAogICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMTAsIDEwLCAyMCwgMTApKSArCiAgZmFjZXRfZ3JpZChjb2xzID0gdmFycyhjZWxsX3R5cGUpLCByb3dzID0gdmFycyhkYXRhc2V0KSkgKwogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKC0xLjUsIDEuNSkpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImRvZGdlcmJsdWUyIiwgImZpcmVicmljazIiKSkgKwogIGd1aWRlcyhjb2xvciA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9IE5VTEwpKSArCiAgc3RhdF9jb3IoYWVzKGxhYmVsID0gcGFzdGUoLi5yLmxhYmVsLi4sIC4ucC5sYWJlbC4uLCBzZXAgPSAifiIpKSwgY29sb3IgPSAiYmxhY2siLCBnZW9tID0gImxhYmVsIikKCgpmaWd1cmVfNQoKI1NhdmUgdG8gcGxvdHMgZm9sZGVyCm91dHB1dF9maWxlbmFtZSA8LSAifi9jZWxsX3Byb3BfcHN5Y2hpYXRyeS9wbG90cy9maWd1cmVfNS5qcGciCgojIFVzZSBnZ3NhdmUgdG8gc2F2ZSB0aGUgcGxvdCBhcyBhIEpQRyBpbWFnZQpnZ3NhdmUob3V0cHV0X2ZpbGVuYW1lLCBmaWd1cmVfNSwgd2lkdGggPSA5LCBoZWlnaHQgPSA3LCBkcGkgPSA1MDApCmBgYAoK